数据结构(邓俊辉)学习笔记】串 06——KMP算法:构造next[]表

ops/2024/10/21 11:46:09/

文章目录

  • 1. 递推
  • 2. 算法
  • 3. 实现

1. 递推

在这里插入图片描述
接下来的这节,我们就来讨论 next 查询表的构造算法。我们将会看到非常有意思是, next 表的构造过程与 KMP 主算法的流程在本质上是完全一样的。

在这里,我们不妨采用递推策略。我们只需回答这样一个一般性的问题即可,也就是说由低至高,如果我们已经构造到了 next 表的第 j 项,那么接下来又当如何进而构造出 j + 1 项?

在此,我们需要再次重温 next 表的定义。也就是说这个表中所谓的第 j 项,也就是在模式串长度为 j 的那个前缀中,自我匹配的真前缀与真后缀的最大程度。由此,我们自然就可得知,在数值上, next 表中的任何一项,相对于此前的那一项,至多只可能增长一个单位。通过反证法,这一点不难得到。

进一步地,这个不等式取等号的充量条件是,在模式串 P 中,编号为 j 的字符与它按照 next 表的继任者彼此相等。

比如在这幅图中, P[j] 就是这个字符 x,而它的继任者则为这个字符x。根据 next 表的定义,以这条线为界,P 的这个前缀与它的这个子串必然是完全匹配的。因此,如果 P[j] 与它的继承者也是相等的,这种自匹配的长度自然就会增加一个单位。

  1. 因此,在这种情况下,next 表中的第 j + 1 项,也自然地就应该在此前第 j 项的基础上再递增一个单位。这样我们也就证明了这个充要条件 “当” 的那个方向。
  2. 为了进而再证明"仅当",我们只需考察 P[j] 与它的替代者不相等的情况。比如后者为 y,此时在这个长度为 j + 1 的前缀中,任何一对自匹配的真前缀和真后缀,也必然蕴含着在此前长度为 j 的那个前缀中自匹配的一对真前缀和真后缀。而且新的那对真前缀和真后缀的长度,也必然相对于此前那对要增加一个单位。而由于 next 表中的各项都是对应于自匹配的最大长度,因此,新的自匹配长度绝对不可能超过此前的自匹配长度。

那么倘若 P[j] 果真与它的继任者不等,我们又该如何计算出 next 表中的下一项呢?

2. 算法

在这里插入图片描述
在这里,需要牢牢抓住的要领,依然是 next 表项的那个必要条件,也就是前缀的自相似性。 刚才为了估算出 next 表的第 j + 1项,我们曾经尝试过在第 j 项的基础上去加 1。因为根据刚才所建立的充要条件,只要 P[j] 与它的继任者是相等的,那么的确可以简明地通过加 1 得到下一项。那么即便 P[j] 与它的继任者不相等,这个必要条件依然可以适用,也就是说在这种情况下,为了估算出 next 表的第 j + 1项,下一个值得尝试的位置,依然需要满足自相似的必要条件。

那么,对应的这个前缀的长度,也自然就应该是在此前长度的基础上,再去取一次对应的 next 表项。也就是说,从前缀长度的变化趋势来看,如果此前是将 j 替换为 next[ j ],那么接下来,就应该将next[ j ]替换为 next[next[ j ]]。当然,如果仍有必要,我们还应该将next[next[ j ]]替换为next [next [next [ j ]]] 。这个过程可能持续多步,一旦遇到这样一个相等的替代者,就可以在它所对应的这个前缀长度的基础上,在累进一个单位,即可得到 next 表的下一项。概括而言,在估算 next 表下一项的过程中,我们应该按照这样一个序列依次尝试。

请注意,因为 next 表项对应的都是真前缀与真后缀的长度,所以,对于任何一个 j 而言,其对应的 next 表项都会严格地小于它自己。这就意味着上述这个序列必然是严格递减的,整个算法迟早会收敛并终止,不然最终的结局有可能是非常极端的,也就是说有可能会一直尝试到0号位置。

在上图中,也就相当于模式串经过多次的位移,最终居然越过了 i + 1 本身。按照通常的理解,此时会出现问题,因为接下来与 P[j] 进行比对的那个字符根本就无从谈起。而事实上,这正是我们的哨兵能够大显身手的又一个场合。应该记得这个假想的哨兵是一个通配的字符,所以作为假想的继任者,它必然在逻辑上也可等效为与 P[j] 相等。因此,即使整个计算过程到了这步田地,也必然会因为这次逻辑上的叛等通过而随即终止。

而且此时 next 表中对应的下一项,就应该是在-1的基础上再加 1,也就是取做0。

至此,只要纵观整个计算的过程,我们就不难发现,这实质上就是模式串自己与自己不断匹配的过程。因此,只需基于 KMP 主算法框架略做修改,也自然就可以导出 next 表的递推计算算法。二者的区别实际上无外乎一点,也就是,新的这个算法需要实时输出 next 表的下一项。

3. 实现

next表的构造算法可以具体实现如下:
在这里插入图片描述

正如我刚才所分析的, 其总体框架应该与 KMP 的主算法几乎一样。主要的差别有这么几点。

  1. 首先,入口参数只有模式串自己。这一点不难理解,因为我们刚才讲过,整个 next 表的构造过程就是它自己与自己的匹配。因此在这个场合,P 既是模式串,也是文本串。

  2. 另一点区别在于初始化。我们刚才已经分析过,next 表的首项,也就是第0项,数值必然固定为-1,因此我们不妨首先就完成这一设置。

  3. 接下来是我们已经非常熟悉的 KMP 循环,其中的 if 和 else 分支,分别对应于当前匹配与失配的两种情况。

    按照我们刚才的分析,一旦发现一对新的匹配字符,我们就可以立即得出 next 表的下一项,而且它的数值就应该是在此前一项的基础上在累进一个单位。反过来,如果是失配,根据我们刚才的分析,也只需将当前的尝试位置 t 更新为它所对应的 next 表项。当然,根据刚才已指出的单调性,这个表项当前必然已经计算出来,所以你尽可放心。

这幅图也给出了该算法的一次典型运行过程。假设我们正需要递推地计算出下一项,此时,我们的 P[j] 是这个 x。首先尝试的是 next[j],如果对应的字符与 P[j] 不等,也就对应于循环中的 else 分支,于是我们会将 next [j] 进而替换为 next[next[j]],并且继续用对应的这个字符与 P[j] 进行比对,如果依然不等,我们就需要将 next[next[j]] 进一步地替换为 next[next[next[j]]],在任何一步迭代中,一旦当前的字符与 P[j] 相等,我们就可以立即将下一个 next 表项设置为在这个前缀的长度基础上,再累进一个单位。当然这个迭代的过程有可能会进行很多步,但正如我们刚才所分析的那样,充其量不过迭代到这样一种状态,也就是当假想的那个哨兵与 P[j] 对齐时,必然会随即终止。

至此,我们已经了解了 KMP 算法的基本原理以及相应的计算过程。那么接下来的一个问题自然就是,这个算法的总体时间复杂度是多少呢?是否如我们所期盼的那样,可以控制在线性的范围以内呢?


http://www.ppmy.cn/ops/103912.html

相关文章

ue Rotate to face BB entry转向不对

可能原因: 角色模型没有到正向。 错误: 正确:

书生大模型实战营闯关记录----第十一关:LMDeploy 量化部署进阶实践 KV cache量化部署,W4A16 模型量化和部署

文章目录 1 配置LMDeploy环境1.1 环境搭建1.2 InternStudio环境获取模型1.3 LMDeploy验证启动模型文件 2 LMDeploy与InternLM2.5 2.1 LMDeploy API部署InternLM2.52.1.1 启动API服务器 2.1.2 以命令行形式连接API服务器 2.1.3 以Gradio**网页形式连接API服务器** 2.2 LMDeploy…

Linux磁盘管理

磁盘管理 1、磁盘简介1.1 磁盘接口类型1.2 磁盘分区类型(MBR和GPT) 2、磁盘管理2.1 查看磁盘信息命令2.2 给服务器挂载新硬盘2.2.1、创建分区2.2.2、创建文件系统(格式化)2.2.3、挂载2.2.4、扩展分区和逻辑分区 3、逻辑卷LVM3.1、LVM概念3.2、新建LVM并挂…

基于SSM的咖啡馆管理系统

基于SSM的咖啡馆管理系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringSpringMVCMyBatisJSP工具:IDEA/Ecilpse、Navicat、Maven 系统展示 前台界面 后台界面 摘要 在当前这个信息爆炸的时代,众多行业正经历着…

【开源免费】基于SpringBoot+Vue.JS渔具租赁系统(JAVA毕业设计)

本文项目编号 T 005 ,文末自助获取源码 \color{red}{T005,文末自助获取源码} T005,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 渔…

git clone 别人的项目上传到自己的Gitee或者github仓库

git clone别人的项目 git clone https://github.com/wohuweixiya/yft-design.git 进入该项目内,删除原有的.git信息 rm -r .git 初始化.git git init 将本地代码添加到仓库 git add . git commit -m "提交仓库说明" Github上新建一个和这个clone下来…

82、k8s的service-NodePort端口开放和生命周期

0、单节点服务,以及k8s命令 [rootmaster01 ~]# kubectl create deployment nginx1 --imagenginx:1.22 --replicas3[rootmaster01 ~]# kubectl create deployment nginx1 --imagenginx:1.22 ##创建资源 deployment.apps/nginx1 created[rootmaster01 opt]# kubec…

找到K个最接近的元素(LeetCode)

题目 给定一个 排序好 的数组 arr &#xff0c;两个整数 k 和 x &#xff0c;从数组中找到最靠近 x&#xff08;两数之差最小&#xff09;的 k 个数。返回的结果必须要是按升序排好的。 整数 a 比整数 b 更接近 x 需要满足&#xff1a; |a - x| < |b - x| 或者|a - x| |b -…