前缀函数(KMP 中的 Pi 数组)
我们用 \(s[l\dots r]\) 表示 \(s\) 中由下标 \(l \sim r\) 的字符组成的子串。
前缀函数,即 \(\pi_i\)。其含义是 \(s[1\dots i]\) 的最长 border 长度。若不存在则为 \(0\)。
其中一个字符串的 border 表示它的某个(不等于原串的)子串,使得这个子串在原串的前缀和后缀都出现过。
显然一个字符串的 border 可能有多个,我们要求解的 \(\pi_i\) 是最长的一个(的长度)。
在这求解 \(\pi\) 数组之前,先考虑这样一个问题:如果已经求出了 \(\pi_1 \dots \pi_n\),如何用这些数表示出 \(s\) 的所有 border(的长度)?
首先 \(\pi_n\) 是一个,且是最长的一个。我们考虑顺次求解第二长,第三长等的 border。
如图。根据 \(\pi\) 数组的定义,我们可知图中 \(2\) 段红色所代表的子串是相同的,\(4\) 段蓝色所代表的子串也是相同的。
观察第一段蓝色和最后一段蓝色,发现这又是一个 \(s\) 的 border,且长度仅次于 \(\pi_n\)。因此 \(s\) 的第二长的 border(的长度)为 \(\pi_{\pi_n}\)。
同理不难得出第三长的是 \(\pi_{\pi_{\pi_n}}\),第四长的是 \(\pi_{\pi_{\pi_{\pi_n}}}\),以此类推。当递归到 \(0\) 时就可以结束了。
考虑顺次求解 \(\pi_i\)。即在计算 \(\pi_i\) 前已经求解了 \(\pi_1,\pi_2\dots \pi_{i-1}\),我们希望用这些值计算 \(\pi_i\)。
对于一个 \(s[1\dots i-1]\) 的 border 而言,不妨令其长度为 \(x\),其前缀和后缀的位置分别为 \([1,x]\) 和 \([i-x,i-1]\)。
如果能将这个 border 的长度增加 \(1\),即增加到 \([1,x+1]\) 和 \([i-x,i]\),那么必须要满足 \(s_{x+1}=s_i\)。如果这个条件满足,就意味着 \(s[1,i]\) 存在一个 border 长度为 \(x + 1\)。
我们可以用上面讲的方式枚举 \(x\),即枚举 \(s[1\dots i - 1]\) 的 border(的长度)。因为我们从大到小枚举,所以第一个满足 \(s_{x+1} = s_i\) 的 \(x + 1\) 就是 \(s[1\dots i]\) 的最长 border,即 \(\pi_i\) 的值。否则,若一个这样的 \(x\) 也不存在,则 \(\pi_i = 0\)。
求解 \(\pi_1,\pi_2\dots \pi_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;
}