Typesetting math: 100%

前缀函数 从KMP到AC自动机

-1.参考资料

OI wiki

0.定义

本文中,字符串下标均从1开始,所以可能和 OI wiki 有一些出入。

定义π[i] (前缀函数)表示 s[1i] 的最长公共前缀后缀的长度。

形式化的,有:

π[i]=maxk=0i{k|s[1k]=s[i(k1)i]}

特别的,π[1]=0

1.预处理

首先有一个显然的结论:

π[i+1]π[i]+1

原因是 s[1π[i+1]]=s[i(π[i+1]1)i] 可以推出 s[1π[i+1]1]=s[i(π[i+1]1)i1]

k=π[i+1]1 即得 s[1k]=s[i1(k1)i1]

根据 π[i] 的定义即得 π[i]k=pi[i+1]1,得证。

于是考虑暴力枚举预处理,然后利用字符串哈希来判别字符串是否相等。

复杂度 O(i=1n1π[i]+1π[i]1)=O(n)

但是这个算法不一定是正确的,可能会被卡模数,我们考虑继续优化。

首先根据上面那个结论我们可以得到一个推论,就是 s[1π[i+1]1]=s[i(π[i+1]1)i1] ,因此我们考虑枚举 s[1i1] 的公共前缀后缀。

考虑怎么 O(1) 找到一个公共前缀后缀的前驱(i.e.仅次于这个公共前缀后缀的第二长度)。

设已知 s[1j]=s[ij+1i],要求出 max{j|s[1j]=s[ij+1i],j<j}

s[1j]=s[ij+1i]=s[jjj]

我们发现了

s[1j]=s[jjj]

所以

j=π[j]

Q.E.D.

转移的次数仍然是 O(n) 的,但是因为我们避免了字符串比较,所以总复杂度 O(n)

代码:

void init(char*s,int n,int*nxt){
	nxt[1]=0;
	for(int i=2,j;i<=n;i++){
		while(j&&s[j+1]!=s[i])j=nxt[j];
		if(s[i]==s[j+1])j++;
		nxt[i]=j;
	}
}
posted @   happydef  阅读(328)  评论(2编辑  收藏  举报
编辑推荐:
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
阅读排行:
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· DeepSeek+PageAssist实现本地大模型联网
· 手把手教你更优雅的享受 DeepSeek
· 腾讯元宝接入 DeepSeek R1 模型,支持深度思考 + 联网搜索,好用不卡机!
· 从 14 秒到 1 秒:MySQL DDL 性能优化实战
点击右上角即可分享
微信分享提示