Z函数

Z 函数是的意义是对于字符串的后缀 i,其最长的前缀使得存在原串的一个前缀和它相同。

我个人认为 Z 函数是简单于 KMP 的,因为 KMP 的思想是利用前面的答案递归调用计算新的位置,而 Z 函数是简单的递推,只需要一个原先计算的结果就能得出答案,不需要递归。

Z 函数的核心思想是匹配段思想,我们一个 [i,i+zi1] 称为一个匹配段。而当我们递推计算 Z 函数的时候,我们总是找到当前右端点最靠右的匹配段。

image

然后我们考虑最右匹配段能给我们提供什么信息。它告诉我们 [1,rl+1][l,r] 是相同的。那么对于 j=rl+1(ri)=il+1,有 [j,rl+1][i,r] 相同。

所以,如果 zj<rlzj+2,那么意味着 [j,j+zj1][1,zj] 匹配并且 j+zjzj+1 不匹配。那么对于 zi,这也成立。zi=zj

image

然后,如果 zjrlzj+2,我们就能确定 [j,rl+1] 就是匹配的,[i,r] 是匹配的。但是因为 [i,r] 后面的部分不一定和 rl+1 后面的匹配,所以我们最多直接套用 ri+1 的长度,后面的都需要暴力扩展。

image

我们观察发现,每次暴力扩展,一定在 r 之后进行。也就是每次暴力扩展至少将最右匹配段增加 1,所以暴力扩展的总次数不会超过 O(n)

然后,我们就得到了 Z 函数的完整计算方法。

inline void buildz(){
	int l=1,r=0;z[1]=0;
	rp(i,n)z[i]=0;
	rep(i,2,n){
		if(r>=i)z[i]=max(0,min(r-i+1,z[i-l+1]));
		while(i+z[i]<=n&&s[i+z[i]]==s[z[i]+1])z[i]++;
		if(z[i]>r)l=i,r=i+z[i]-1;
	}
}
posted @   jucason_xu  阅读(550)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示