前缀函数(KMP 中的 Pi 数组)

我们用 s[lr] 表示 s 中由下标 lr 的字符组成的子串。

前缀函数,即 πi。其含义是 s[1i]最长 border 长度。若不存在则为 0

其中一个字符串的 border 表示它的某个(不等于原串的)子串,使得这个子串在原串的前缀和后缀都出现过。

显然一个字符串的 border 可能有多个,我们要求解的 πi 是最长的一个(的长度)。


在这求解 π 数组之前,先考虑这样一个问题:如果已经求出了 π1πn,如何用这些数表示出 s 的所有 border(的长度)?

首先 πn 是一个,且是最长的一个。我们考虑顺次求解第二长,第三长等的 border。

如图。根据 π 数组的定义,我们可知图中 2 段红色所代表的子串是相同的,4 段蓝色所代表的子串也是相同的。

观察第一段蓝色和最后一段蓝色,发现这又是一个 s 的 border,且长度仅次于 πn。因此 s 的第二长的 border(的长度)为 ππn

同理不难得出第三长的是 πππn,第四长的是 ππππn,以此类推。当递归到 0 时就可以结束了。


考虑顺次求解 πi。即在计算 πi 前已经求解了 π1,π2πi1,我们希望用这些值计算 πi

对于一个 s[1i1] 的 border 而言,不妨令其长度为 x,其前缀和后缀的位置分别为 [1,x][ix,i1]

如果能将这个 border 的长度增加 1,即增加到 [1,x+1][ix,i],那么必须要满足 sx+1=si。如果这个条件满足,就意味着 s[1,i] 存在一个 border 长度为 x+1

我们可以用上面讲的方式枚举 x,即枚举 s[1i1] 的 border(的长度)。因为我们从大到小枚举,所以第一个满足 sx+1=six+1 就是 s[1i] 的最长 border,即 πi 的值。否则,若一个这样的 x 也不存在,则 πi=0

求解 π1,π2πn 的代码:

int ne[N];		// ne[i] 就是 pi[i],即 s[1...i] 的最长 border(的长度)

ne[0] = ne[1] = 0;
for (int i = 2; i <= n; ++ i ) {
	int j = ne[i - 1];
	while (j && s[j + 1] != s[i]) j = ne[j];
	if (s[j + 1] == s[i]) j ++ ;
	ne[i] = j;
}
posted @   2huk  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示