字符串板刷
给定一个长为 的字符串 ,然后给定 个长度不超过 的模式串。
考虑所有的 ,问有几个模式串在至少一个 中出现.
对于每个模式串独立处理。
定义后缀函数:模式串和文本串全部反向后的前缀函数。
首先求得每个模式串的前缀函数 ,再求得后缀函数 .
如果 中存在位置 ,满足 ,其中 是字符串长度,那么显然这个字符串会在一个 中出现。
有一个显然(我为什么没想到 )的贪心性质是:我们只需要计算出pref的前缀最大值
,以及suff的后缀最大值
,然后看
是否大于
即可。
总结:
- 认真读题
- 当出现了 的前缀函数值时,之前一定出现过
- 长度为 的字符串无法被分割。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 100016, MOD = 1000000007;
char tex[M], pat[M];
int fail[M], pref[M], suff[M];
void make_fail()
{
for(int i=1, j=0; pat[i]; ++i)
{
while(j && pat[i]!=pat[j]) j=fail[j-1];
fail[i] = pat[i]==pat[j] ? ++j : 0;
}
}
void search(int *arr) //求文本串对模式串的前缀函数
{
for(int i=0, j=0; tex[i]; ++i)
{
while(j && tex[i]!=pat[j]) j=fail[j-1];
arr[i] = tex[i]==pat[j] ? ++j : 0;
}
for(int i=1; tex[i]; ++i)
arr[i] = max(arr[i], arr[i-1]);
}
bool check(int n)
{
int m = strlen(pat);
if(m==1) return 0;
make_fail(); search(pref);
reverse(tex, tex+n); reverse(pat, pat+m);
make_fail(); search(suff);
reverse(tex, tex+n); reverse(pat, pat+m);
reverse(suff, suff+n);
for(int i=0; i<n-1; ++i)
if(pref[i]+suff[i+1]>=m)
return 1;
return 0;
}
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
scanf("%s", tex);
int n = strlen(tex), q = read(), ans = 0;
for(int i=1; i<=q; ++i)
{
scanf("%s", pat);
if(check(n)) ++ans;
}
printf("%d\n",ans );
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}