首页 > 其他分享 >扩展 KMP/exKMP(Z 函数)学习笔记

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

时间:2024-07-18 19:08:59浏览次数:13  
标签:exKMP 匹配 函数 求解 笔记 KMP sim

声明

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

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

LCP:最长公共前缀。

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

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

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

假设现在已经求出了 \(z_1\sim z_{i-1}\),想要求解 \(z_i\)。

定义 \(r\) 为 \(r\) 最大的匹配子串的右端点,\(l\) 为 \(r\) 最大的匹配子串的左端点。

那么如果 \(i\le r\),\(z_i\) 为 \(\min(z_{i-l+1},r-i+1)\)。通俗的解释一下:由定义知,\(s_{l}\sim s_{i-1}\) 与 \(s_1\sim s_{i-l}\) 是匹配的,则
\(s_i\sim s_r\) 与 \(s_{i-l+1}\sim s_{r-l+1}\) 是匹配的,那么 \(z_i\) 可以由 \(z_{i-l+1}\) 继承而来。但是我们并不知道 \(s_{r+1}\) 与 \(s_{r-i+2}\) 是否匹配,所以 \(z_i\) 需要对 \(z_{i-l+1}\) 和 \(r-i+1\) 取 \(\min\)。

接着,我们尝试进行暴力匹配,如果 \(s_{i+z_i}\) 与 \(s_{z_i+1}\) 相同,就让 \(z_i\) 加 \(1\)。

暴力匹配完后更新 \(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
}

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

则类似的,可以得到求解 \(p_i\) 的代码:

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\),则输出 \(cnt_x\)。

完整代码:

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 函数) 的题解区学习。

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

标签:exKMP,匹配,函数,求解,笔记,KMP,sim
From: https://www.cnblogs.com/Lydic/p/18310265/exkmp

相关文章

  • Datawhale AI 夏令营——CPU部署大模型(LLM天池挑战赛)——Task2与3学习笔记
        Task2的任务是组队+寻找灵感,这里不作阐述;Task3的任务是实现RAG应用,阅读文档并观看卢哥的直播后,结合个人经验做个分享。    运行大语言模型,对LLM使用的加深,我们发现,在使用过程中,大模型会有很多幻觉出现。为了解决幻觉,科研人员提出了各种各样的方案......
  • 什么是信息指纹和信息加密——《数学之美》第16、17章以及其他各种资料的读书笔记
    目录1.信息指纹1.1概念1.2相关算法的演进历程1.3 哈希碰撞1.4 雪崩效应1.5 应用场景2.信息加密2.1密码学的简要历史2.1.1古代密码学:智慧的萌芽2.1.2 中世纪至文艺复兴:密码术的兴起2.1.3 近代密码学:机械密码机的诞生2.1.4 现代密码学:复杂科学的诞生2.......
  • JavaWeb笔记_Response对象
    一.Response对象1.1Response对象概述a.专门负责给浏览器响应信息(响应行,响应头,响应体)的对象b.我们主要使用的是跟HTTP协议相关的Response对象:HTTPServletResponse,继承了ServletResponse,扩展了ServletResponse接口,提供了更多的方法,例如可以操作响应头,cookie等1.2Response......
  • OI学习笔记(C++)
    笔记完整版链接参照oi.wiki整理了基础dp的一些笔记:学习笔记+模板(Adorable_hly)(自己结合网络和做题经验总结的,dalao勿喷)第一大板块:DP动态规划适用场景:1.最优化原理:若该问题所包含的子问题解是最优的,就称该问题具有最优子结构,满足最优化原理。2.无后效性:指某一阶段的状......
  • HP笔记本驱动安装教程
    HP电源管理器类型:软件-解决方法说明:HP电源管理器提供了基础的电源轻松管理软件,该软件支持本地或远程管理员管理峰值电源设置。该软件包适用于运行受支持的操作系统的受支持计算机系统。修复和增强:-更新了HPPowerManager软件。-提供了ICS检测与安装。-将Fusio......
  • [MAUI 项目实战] 笔记App:程序设计
    前言有人说现在记事类app这么多,市场这么卷,为什么还想做一个笔记类App?一来,去年小孩刚出生,需要一个可以记录喂奶时间的app,发现市面上没有一款app能够在两步内简单记录一个时间,可能iOS可以通过备忘录配合捷径做到快速记录,但是安卓上就没有类似的app。二是,自去年做的音乐播放器以来......
  • 设计模式之适配器模式(学习笔记)
    定义 适配器模式是一种结构型设计模式,它允许将一个类的接口转换为客户端希望的另一个接口。适配器使得原本由于接口不兼容而不能一起工作的类可以协同工作。通过创建适配器类,可以将现有类的接口转换成目标接口,从而使这些类能够在一起工作。为什么使用适配器模式兼容性适......
  • 吴恩达深度学习课程笔记Lesson03
    第三周:浅层神经网络(Shallowneuralnetworks)文章目录第三周:浅层神经网络(Shallowneuralnetworks)3.1神经网络概述(NeuralNetworkOverview)3.2神经网络的表示(NeuralNetworkRepresentation)3.3计算一个神经网络的输出(ComputingaNeuralNetwork'soutput)3.4多样......
  • xxe学习笔记
    什么是xxeXXE(XMLExternalEntityInjection)全称为XML外部实体注入,由于程序在解析输入的XML数据时,解析了攻击者伪造的外部实体而产生的。例如PHP中的simplexml_load默认情况下会解析外部实体,有XXE漏洞的标志性函数为simplexml_load_string()。当允许引用外部实体时,通过构造恶......
  • python笔记:赋值,浅拷贝和深拷贝
    在Python中,变量赋值、浅拷贝和深拷贝在处理对象时有不同的行为和应用场景。以下是它们的详细区别:1.赋值赋值操作只是创建了一个新的引用(别名)来指向同一个对象。也就是说,赋值操作并不创建新的对象,原始对象和赋值后的变量指向同一块内存区域。a=[1,2,3]b=a#b是a......