题目大意:
题目链接:
USACO:http://train.usaco.org/usacoprob2?a=dLE0hVDUyv1&S=prefix
洛谷:https://www.luogu.org/problemnew/show/P1470
给出多个子串和一个字符串,求该字符串的前多少位可以完全被子串覆盖掉。
思路:
很多人都说用和搜索,但是我怎么看都是。
我们可以在的时间复杂度内求出一个元素在序列里的位置,那么可以用前缀和的思想,用一个数组记录答案,找到一个位置后,将头的位置的答案,尾的位置的答案。对所有元素进行一边改操作,时间复杂度,其中表示元素个数。
然后以跑一遍前缀和,此时如果前个数大于,答案就是。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int M=210;
const int N=200100;
int n,m,j,sum=1,next[20],ans[N];
char a[N],b[M][20],c[M];
int main()
{
while (cin>>b[sum]+1&&b[sum][1]!='.') sum++;
while (cin>>c) //太菜不知道有什么更好的读入方法
for (int i=0;i<strlen(c);i++)
a[++n]=c[i];
for (int k=1;k<sum;k++)
{
memset(next,0,sizeof(next));
m=strlen(b[k]+1);
j=0;
next[1]=0;
for (int i=1;i<m;i++) //求next
{
while (j&&b[k][j+1]!=b[k][i+1]) j=next[j];
if (b[k][j+1]==b[k][i+1]) j++;
next[i+1]=j;
}
j=0;
for (int i=0;i<n;i++) //KMP
{
while (j&&b[k][j+1]!=a[i+1]) j=next[j];
if (b[k][j+1]==a[i+1]) j++;
if (j==m)
{
ans[i+2]--;
ans[i-m+2]++;
j=next[j];
}
}
}
for (int i=1;i<=n;i++)
ans[i]+=ans[i-1]; //跑一遍前缀和
for (int i=1;i<=n;i++)
if (ans[i]<=0)
{
printf("%d
",i-1);
return 0;
}
printf("%d
",n);
return 0;
}