Z 函数
概述
Z 函数记录字符串与各个后缀的最长公共前缀长度,国内常把计算它的算法称为扩展 KMP。算法维护当前右端最靠右的匹配段;若待求位置落在段内,就先复用对应位置的信息,再按需继续向右扩展;否则从头扩展。由于每次扩展都会推动右端点,总复杂度为 O(n)。应用包括用分隔符拼接模式串与文本后进行匹配,以及判断字符串的周期与最小循环节。
参考资料
简介
对于长度为 的字符串 ,定义 Z 函数(Z-function) 为 与 的最长公共前缀(LCP)的长度,特别地 。国内通常称计算该数组的算法为 扩展 KMP(exKMP)。
维护当前右端点最靠右的匹配段 ,则 是 的前缀。计算 时:若 ,则 ;否则从头暴力扩展。每次扩展都使 至少右移 1,总时间复杂度为 。
实现
#include <bits/stdc++.h>
using namespace std;
const int N=500005;
int z[N];
void z_func(string s)
{
int n=s.size();
z[0]=0;
for(int i=1,l=0,r=0;i<n;i++)
{
z[i]=i<=r?min(z[i-l],r-i+1):0;
while(i+z[i]<n&&s[z[i]]==s[i+z[i]])z[i]++;
if(i+z[i]-1>r)l=i,r=i+z[i]-1;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
cin>>s;
z_func(s);
int n=s.size();
for(int i=0;i<n;i++)cout<<z[i]<<' ';
return 0;
}
应用
模式匹配:求模式串 在文本 中的所有出现位置。取分隔符 #(不在两串中),对 求 Z 函数,凡 的位置都对应 中的一次匹配。
最小循环节:枚举 ,若 ,则 的前 个字符构成一个周期;最小的这样的 即最小循环节长度。若还满足 ,则 由该循环节整数次重复而成。