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);
}

浙公网安备 33010602011771号