给定两个整数序列 A , B ( 2 e 5 ) A,B(2e5) A,B(2e5),求 B B B的差分序列在 A A A的差分序列中的出现次数.
首先如果 B B B的长度是 1 1 1,答案就是 A A A的长度。
否则做差分,然后整数KMP.
题目一点不难,但是写了几次都有问题,很多问题都在于,KMP算法很多地方都用了字符串结尾是\0
这个性质,但是整数KMP是不能这样的。
重新总结一套鲁棒性强的板子。需要注意的是if之后新的前缀函数值不一定是j,因为如果j=w,会被缩减一次。
for(int i=1, j=0; i<w; ++i)
{
while(j && pat[i]!=pat[j]) j=fail[j-1];
fail[i] = pat[i]==pat[j] ? ++j : 0;
}
int ans = 0;
for(int i=0, j=0; i<n; ++i)
{
while(j && tex[i]!=pat[j]) j=fail[j-1];
if(tex[i]==pat[j] && ++j==w) ++ans, j=fail[j-1];
}
printf("%d\n",ans );
完整代码
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 200016, MOD = 1000000007;
int tex[M], pat[M], fail[M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read(), w = read();
if(w==1)
{
printf("%d\n",n );
return 0;
}
for(int i=0; i<n; ++i) tex[i] = read();
for(int i=0; i<w; ++i) pat[i] = read();
n--, w--;
for(int i=0; i<n; ++i) tex[i] = tex[i+1] - tex[i];//printf("%d",tex[i] );printf("\n");
for(int i=0; i<w; ++i) pat[i] = pat[i+1] - pat[i];//printf("%d",pat[i] );printf("\n");
tex[n] = pat[w] = 0;
for(int i=1, j=0; i<w; ++i)
{
while(j && pat[i]!=pat[j]) j=fail[j-1];
fail[i] = pat[i]==pat[j] ? ++j : 0;
}
int ans = 0;
for(int i=0, j=0; i<n; ++i)
{
while(j && tex[i]!=pat[j]) j=fail[j-1];
if(tex[i]==pat[j] && ++j==w) ++ans, j=fail[j-1];
}
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;
}