数据结构与算法基础(王卓)(17):KMP算法详解(精讲(最简单直接有效的思路方法))
创始人
2024-06-02 14:41:41
0

本文具体思路参考: (最后证明,该教材/网课实际上是最有效的)

DS第四章【3】KMP1_哔哩哔哩_bilibili


中间走的一些弯路的教材: 

第06周05--第4章串、数组和广义表5-4.3串的操作--串的匹配算法2--KMP算法_哔哩哔哩_bilibili

课本

走弯路过程,详见


 PART 1:关于next [ j ]

PPT:P30 (根据DS第四章【3】KMP1_哔哩哔哩_bilibili)


需求:

返回子串和主串匹配时候的位置


思想:

把上面的主串((也就是)箭头左边的(前面的)那一部分)当成箭头左边的子串的一个分身

注:比较到不匹配时,箭头(指针)指向不匹配的字符

他们之间公共的部分:

要么是完全对其时候的全部

要么是下面的子串移动到其(主串)公共后缀的位置的时候

其他上下两个串摆放的位置,都不可能产生我们想要的,公共的部分

其实分析这个问题的时候我们就已经不用去看主串的位置和情况了,因为其实我们已经知道:

在匹配不上的前面(箭头左边),子串和主串都是一样的,所以其实只要看子串就行了

所以我们只需要找出前面(箭头左边)的公共前后缀

然后往前(右)移动子串,让他的前缀移动到原来后缀的位置

就可以解决这个问题,往下比较了


核心:

直接把前缀移动到(移动)之前后缀所处的位置,跳过这中间所有的字符


好了,现在我们思路有了,接下来:

具体落实怎么移动:

移动目的:找到主串子串完全匹配的位置

如果发生的是主串和子串比较的字符匹配的情况,我觉得我们根本不用讨论

现在比较的这一位匹配,那就比较下一位

如果一路匹配下去一直都是能匹配的上的话,那就返回结果:

子串放在第一格,可以和主串匹配上

就行了

所以我们需要讨论解决的,是当每一位发生了不匹配的操作时,我们怎么相对应的处理

另外,我们这里不仅要知道下一步该具体怎么移动,还需要思考:

如何用指针实现进一步的比较(所有的探究、思路,都是为了写出程序服务)

现在,我们知道了当每一步(每一种)情况,我们应该怎么进行操作

根据上图,我们就得到了下一步如何操作(把子串的指针移动到哪个位置的操作)的

公式:

字符串从位序 1 开始存储时:

PPT(网课)上的归纳公式

字符串从位序 0 开始存储时:

书上的公式

以上都是怎么进行移动的公式、思路、理论,接下来我们是要写代码的:

先不写KMP算法代码,我们先写求:每一位匹配不上情况的next(把子串的指针移动到哪个位序)

当我们一个一个排(匹配)下去,显然,当我们比较(两个字符)的时候:

  1. 这个字符前面的所有字符都应该是匹配的,如果不匹配,我们就移动子串的指针位置,重新从头开始比较
  2. 这个字符前面的所有字符的 next 数组的信息,我们都是可以知道的

所以

现在,我们每往下走一步面临的问题就是:

主串和子串比较的字符匹配【if(1)】,讨论无意义

所以讨论不匹配时【if(0)】,怎么处理:我们面临的情况:

再次强调:

 我们要把思路转变一下

不是说我们求的是next【j】而是我们求的是next【j+1】

我们需要的是从过去推出未来,而不是说

未来是什么我不知道,然后我再去猜过去式这种情况还是那种情况

那种的情况(不等)又只知道一半

只知道最后面一个字符不匹配,在前面的就不知道了

这样研究的话,那还不如具象化的研究

不要让自己走向未知的方向。走进越来越未知的方向

从已知走向未知,让未知走向已知


把上面的这个模式串(子串)分身【位序 j - k 到 k - 1 】看成主串

把下面的这个模式串(子串)分身【位序 0 到 k - 1 】看成子串

于是该问题转化为:

主串与模式串最后一个字符不匹配

然后同样的,比较;比较什么:

子串里有没有公共前后缀

确定后面该移到哪个位置

特别注意:

不要遗漏已知条件:(这个条件和之前主串子串匹配同样类似/相似)

在我们这个不匹配的字符前面,所有的字符全部都匹配

现在我们回到解决问题的

核心:子串(模式串)该移到哪个位置


kmp算法:

同样的,找到这里子串【0到k】前面的公共前后缀

然后把前缀移动到后缀的位置,看看新的前缀后面的字符能不能和第 j 位匹配得上

操作上面的图已经写了:

子串指针移动到【k + 1】位序


bf算法:

如果我们用bf算法,也就是说一格一格往右边移

主串位置不变,子串一格一格往右边移动一次一次比较直到匹配成功到像前缀后缀一样为止

那么操作就是:

k--


再次强调:

下一位字符匹配之前是不用我们去担心他(下一位)和主串之前是不是匹配得上的这个问题的

因为既然能够匹配得到他(这个字符),那就已经说明了他前面的每一位字符都是匹配得上的


疑问:

为什么不从中间开始匹配(如果中间有和后缀一样的,但是开头没有呢,那我们要求必须从开头开始算前缀是不是容易漏掉一些可能的正确答案呢):

答案:

如果中间有,但是开头不行,那最后子串中间的字符是匹配了,但是中间的 前面的那部分字符终究还是不匹配,那迟早要完蛋,终究是失败的

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...