Z 函数(扩展 KMP)

规定字符串下标从 0 开始。

\(z[i]\) 表示从 \(i\) 开头的最长子串使得该子串为 \(s\) 的前缀。
特别地,\(z[0]=0\)

求法

类似 manacher,维护 \(r=\max \{i+z[i]-1\}\) 以及它对应的 \(l=i\)
借助之,我们从 \(1\sim n-1\) 枚举 \(i\)

  • \(i\le r\),则根据定义 \(s[i..r]=s[i-l..r-l]\),若 \(z[i-l]<r-i+1\),则 \(z[i]=z[i-l]\)
  • 否则,令 \(z[i]=\max(0,r-i+1)\),暴力尝试扩展 \(z[i]\),即向右移动 \(r\)
  • \([i,i+z[i]-1]\) 更新 \(l,r\)

可见,复杂度等于 \(r\) 向右移动的总量 \(=n\)

z[0]=0;
int l=0,r=0;
for(int i=1;i<n;i++){
	if(i<=r&&z[i-l]<r-i+1)z[i]=z[i-l];
	else {
		z[i]=max(0,r-i+1);
		while(i+z[i]<n&&s[i+z[i]]==s[z[i]])z[i]++;
	}
	if(r<i+z[i]-1)l=i,r=i+z[i]-1;
}

练习:CF432D Prefixes and Suffixes

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
char s[N];
int n,z[N],siz[N],nxt[N];
vector<int>G[N];
void dfs(int x){
	siz[x]=1;
	for(int y:G[x])dfs(y),siz[x]+=siz[y];
}
int main(){
	scanf("%s",s),n=strlen(s);
	z[0]=0;
	int l=0,r=0,cnt=0;
	for(int i=1;i<n;i++){
		if(i<=r&&z[i-l]<r-i+1)z[i]=z[i-l];
		else {
			z[i]=max(0,r-i+1);
			while(i+z[i]<n&&s[i+z[i]]==s[z[i]])z[i]++;
		}
		if(r<i+z[i]-1)l=i,r=i+z[i]-1;
		if(i+z[i]==n)cnt++;
	}
	nxt[1]=0;
	for(int i=2,j=0;i<=n;i++){
		while(j&&s[i-1]!=s[j])j=nxt[j];
		if(s[i-1]==s[j])j++;
		nxt[i]=j;
	}
	for(int i=1;i<=n;i++)G[nxt[i]].push_back(i);
	dfs(0);
	printf("%d\n",cnt+1);
	for(int i=n-1;i;i--){
		if(i+z[i]==n){
			printf("%d %d\n",z[i],siz[z[i]]);
		}
	}
	printf("%d %d\n",n,1);
}
posted @ 2022-07-12 22:13  pengyule  阅读(142)  评论(0)    收藏  举报