exkmp/Z函数
扩展 KMP/exKMP(Z 函数)
首先我们求出 \(ne\) 数组。代表 \(b\) 与 \(b\) 的每一个后缀的最长公共前缀长度。
我们设当前要求 \(ne_x\),且 \(k\) 为使得 \(p=k+ne_k-1\) 最大的位置且 \(0\le k<x\),同时 \(p\) 为 \(k+ne_k-1\)。
于是我们得到了两个蓝块相同。
再通过这个图,得出两个绿块相等。接下来我们设 \(l\) 为 \(ne_{x-k}\)。
这里由于 \(ne_{x-k}\) 的定义,最前面两个灰块一定相等。又因为两个绿块相等,且后两个灰块都是绿块的前缀,所以后两个灰块相等,所以所有灰块相等。
当 \(x+l\le p\) 时也就是上图的情况,此时的 \(ne_x=l\),因为 \(ne_{x-k}\) 已经达到了最大。
当 \(x+l>p\) 时,考虑能够判定相等的部分(绿块)已经被全部包含,所以考虑绿块后面的部分即可。发现没有更好的办法,于是我们直接暴力匹配。
由于暴力匹配每次会右移绿块右边界,而显然最多右移 \(n\) 次,于是这样是线性的。
所以从 \(p-x+1\) 和 \(p+1\) 开始暴力匹配即可。
第二个数组自行类比 \(ne\) 数组的求法即可。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 20000005
using namespace std;
int n,m,z[N],p[N];
char a[N],b[N];
void Z(char s[N],int n){
z[1]=n;
int l=0,r=0;
for(int i=2;i<=n;i++){
if(i<=r)z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&s[i+z[i]]==s[z[i]+1])z[i]++;
if(i+z[i]-1>r){
l=i;
r=i+z[i]-1;
}
}
}
void exkmp(char s[N],int n,char t[N],int m){
Z(t,m);
int l=0,r=0;
for(int i=1;i<=n;i++){
if(i<=r)p[i]=min(z[i-l+1],r-i+1);
while(i+p[i]<=n&&s[i+p[i]]==t[p[i]+1])p[i]++;
if(i+p[i]-1>r){
l=i;
r=i+p[i]-1;
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>a+1>>b+1;
n=strlen(a+1);
m=strlen(b+1);
exkmp(a,n,b,m);
int res=0;
for(int i=1;i<=m;i++){
res^=(i*(z[i]+1));
}
cout<<res<<'\n';
res=0;
for(int i=1;i<=n;i++){
res^=(i*(p[i]+1));
}
cout<<res<<'\n';
return 0;
}