Z函数(扩展KMP)学习笔记
待补
upd on 2023.11.6
鸽了半年来补这个学习笔记
起因是觉得自己点的技能点已经远远不够了,所以重新开始点技能点
之后希望所有学的内容都要写学习笔记以防自己忘了
Z 函数的定义:Z[i] 表示 s 本身与 si 开头的子串的最长公共前缀。同时规定 Z0=0
Z[aaaaa]={0,4,3,2,1}
Z[abacaba]={0,0,1,0,3,0,1}
如何求解 Z 函数呢?我们设之前被匹配过的最靠右的一个右端点为 r 指针。当我们要计算 Zi 时:
如果 i>=r,暴力匹配。
如果 i<r,那么子串 [i,r] 一定与 s 开头的某个子串是相等的(不妨设匹配到 r 的那一位是 l,即 Z[l]=r-l+1,那么显然有 s[i..r]=s[i-l+1..r-l+1]。所以 [i..r] 与 [1..r-i+1] 的匹配就相当于 s[i-l+1..r-l+1] 与 [1..r-i+1] 的匹配,就是 Zi-l+1。)那么考虑:如果 Zi-l+1 小于 r-i+1,那说明 [i..r] 一段都不能完全匹配,所以直接赋值 Zi=Zi-l+1。否则,r 右边的部分继续暴力匹配。
时间复杂度是显然的 O(|S|),因为 r 指针正好扫完了一遍字符串。
例题 P2375
显然对于每一个位置 i,都满足对于所有 k<Zi 有 s[0..k]=s[i..i+k]。如果这里的 k<i,那么这一组 k 和 i 就对 num[i+k] 产生了 1 的贡献。
所以计算出原字符串的 z 函数值,然后用查分数组维护即可。