版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/84471356
题目大意:
题目链接:
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\n",i-1);
return 0;
}
printf("%d\n",n);
return 0;
}