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\)

于是我们得到了两个蓝块相同。

image

再通过这个图,得出两个绿块相等。接下来我们设 \(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;
}
posted @ 2024-09-04 20:58  zxh923  阅读(1)  评论(0编辑  收藏  举报