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 函数值,然后用查分数组维护即可。

posted @ 2023-05-11 08:45  1358id  阅读(59)  评论(0编辑  收藏  举报