扩展 KMP/exKMP(Z 函数)学习笔记

声明

本文章转载自 shangruolin 的博客,已经过作者(存疑)同意,帮TA宣传一下。

扩展 KMP/exKMP(Z 函数)学习笔记兼 P10479 匹配统计 题解。

LCP:最长公共前缀。

Z 函数,又称扩展 KMP(exKMP),能够在 O(n) 的时间内求出一个字符串与其所有后缀的 LCP 的长度。

定义 zi 为字符串 s 与字符串 s 从第 i 位开始的后缀的 LCP 长度。

那么求解 zi 便是我们的目标。直接暴力匹配复杂度为 O(n2)。二分枚举长度再用哈希判断可以做到 O(nlogn),足以通过本题。使用 Z 函数求解便可做到 O(n) 求解。

假设现在已经求出了 z1zi1,想要求解 zi

定义 rr 最大的匹配子串的右端点,lr 最大的匹配子串的左端点。

那么如果 irzimin(zil+1,ri+1)。通俗的解释一下:由定义知,slsi1s1sil 是匹配的,则
sisrsil+1srl+1 是匹配的,那么 zi 可以由 zil+1 继承而来。但是我们并不知道 sr+1sri+2 是否匹配,所以 zi 需要对 zil+1ri+1min

接着,我们尝试进行暴力匹配,如果 si+ziszi+1 相同,就让 zi1

暴力匹配完后更新 l,r 即可。

Z 函数的复杂度是 O(n) 的,因为每个字符至多被暴力匹配 1 次。

求解 z 数组的代码:

z[1] = m; // 完全匹配
for (int i = 2, l = 0, r = 0; i <= m; i++) {
	if (i <= r) z[i] = min (z[i - l + 1], r - i + 1); // 获取初值
	while (i + z[i] <= m && b[i + z[i]] == b[z[i] + 1]) z[i] ++; // 暴力匹配
	if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1; // 更新 l, r
}

同样,我们定义 pi 为为字符串 b 与字符串 a 从第 i 位开始的后缀的 LCP 长度。

则类似的,可以得到求解 pi 的代码:

for (int i = 1, l = 0, r = 0; i <= n; i++) {
	if (i <= r) p[i] = min (z[i - l + 1], r - i + 1);
	while (i + p[i] <= n && a[i + p[i]] == b[p[i] + 1]) p[i] ++;
	if (i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
	cnt[p[i]] ++; // 统计答案
}

对于每个询问,若询问长度为 x,则输出 cntx

完整代码:

for (int i = 1, l = 0, r = 0; i <= n; i++) {
	if (i <= r) p[i] = min (z[i - l + 1], r - i + 1);
	while (i + p[i] <= n && a[i + p[i]] == b[p[i] + 1]) p[i] ++;
	if (i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
	cnt[p[i]] ++; // 统计答案
}

本文没有图解,对初学者来说可能比较抽象。想要更好理解 Z 函数的同学可以前往 P5410 【模板】扩展 KMP/exKMP(Z 函数) 的题解区学习。

大家有任何建议或疑惑都可以在评论中提出,感谢!

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