Z 函数

太菜了,今天才刚学会。

题目链接

Z 函数 定义。

对于一个长度为 \(n\) 的字符串 \(s\),定义函数 \(z[i]\)\(s\)\(s[i,n-1]\)(即以
\(s[i]\) 开头的后缀)的最长公共前缀(LCP)的长度,则 \(z\) 被称为 \(s\) 的 Z 函数。
——OI Wiki

Solution

Part 1 暴力碾标算

考虑暴力,很容易可以想道 \(O(n^2)\) 的做法:

z[1]=strlen(s+1);
for(int i=2;i<=strlen(s+1);i++){
	for(int j=1,k=i;k<=strlen(s+1);k++,j++){
		if(s[j]!=s[k]){	
			z[i]=j-1;
			break;
		}
		z[i]=j; 
	}
} 

只能拿到 \(29\) 分。

Part 2 正解

考虑跟 \(Manacher\) 一样的思路:

\(S_1\) 表示原字符串 \(s\)\(S_i\) 表示以第 \(i\) 个字符为开头的 \(S\) 后缀,\(S_{max}\) 表示与 \(S\)\(LCP\) 最长的后缀)

QQ20250805-201407

我们规定红色线段为 \(S\) 后缀中 \(LCP\) 的右端点目前最远的 \(LCP\)

那么 \(r\) 表示右端点, \(l\) 表示左端点。

再解释一下。

设以红色线段为 \(LCP\) 的后缀为 \(S_{max}\),以第 \(j\) 个节点为开头的后缀为 \(S_j\),一个后缀 \(s\)\(LCP\) 右端点为 \(R_{s}\) 左端点为 \(L_{s}\),则:

$R_{S_{max}} = max_{j }^{i-1} R_{S_j} $

\(r=R_{S_{max}},l=L_{S_{max}}\)

那么我们这的绿色线段表示以 \(i\) 为左端点,\(r\) 为右端点的 \(S\) 子串。

可以发现 $S $ 会有一段相对应的绿色线段,由于与 \(S_max\) 相对应,所以左端点为 \(l-i+1\)

那么就肯定会有一个后缀 \(S_{l-i+1}\) 使得前缀为绿色线段,那么 \(S_i\) 的 Z 函数就可已从 \(S_{l-i+1}\) 来转移,因为 \(S_{l-i+1}\)\(S_i\) 都是绿色前缀。

类似 Manacher 算法,我们分为两种情况:

  1. \(Z[l-i+1]\) 小于等于 \(r\) 那么我们可以直接继承过来。 (即蓝色段)

  2. \(Z[l-i+1]\) 大于 \(r\) 那么我们只能取 \(i\)\(r\) 的这段,因为外面段我们不能保证相同。 (即紫色段)

继承后,我们再暴力一下,得到准确的答案。

最后还需要更新下 \(r\)\(l\)

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e7+10;
char a[N],b[N];
int ans;
int z[N];
int p[N];
signed main(){
	ios::sync_with_stdio(false);
	cin>>(a+1)>>(b+1);
	int l=0,r=0;
	z[1]=strlen(b+1);
	for(int i=2;i<=strlen(b+1);i++){
		if(i<r) z[i]=min(z[i-l+1],r-i+1);
		while(i+z[i]<=strlen(b+1)&&b[i+z[i]]==b[z[i]+1]) {
			z[i]++;
		}
		if(r<i+z[i]-1){
			r=i+z[i]-1;
			l=i;
		}
	}
	for(int i=1;i<=strlen(b+1);i++){
		ans=ans^(i*(z[i]+1));
	}
	cout<<ans<<endl;
	l=0,r=0;
	for(int i=1;i<=strlen(a+1);i++){
		if(i<r) p[i]=min(z[i-l+1],r-i+1);
		while(i+p[i]<=strlen(a+1)&&a[i+p[i]]==b[p[i]+1]) {
			p[i]++;
		}
		if(r<i+p[i]-1){
			r=i+p[i]-1;
			l=i;
		}
	}
	ans=0;
	for(int i=1;i<=strlen(a+1);i++){
		ans^=(i*(p[i]+1));
	}
	cout<<ans;
}
posted @ 2025-08-05 21:24  Distant_Cloud  阅读(29)  评论(0)    收藏  举报