kmp模式匹配算法例题(kmp算法next计算方法)

KMP算法是一种字符串匹配算法,可以在O(n+m)的时间复杂度内实现两个字符串的匹配。本文将引导您学习KMP算法,阅读大约需要30分钟。 字符串匹配问题 所谓字符串匹配,是这样一种问题:“字符串P是否为字符串S的字串?如果是,它出现在S的哪些位置?”其中S称为主串;P称为模式串。下面的图片展示了一个例子。 主串是莎翁那句著名的“tobeornottobe”,这里删去了空格。“no”这个模式串的匹配…

KMP算法是一种 字符串匹配 算法,可以在 O(n+m) 的时间复杂度内实现两个字符串的匹配。本文将引导您学习KMP算法,阅读大约需要30分钟。

字符串匹配问题

所谓字符串匹配,是这样一种问题:“字符串 P 是否为字符串 S 的字串?如果是,它出现在 S 的哪些位置?” 其中 S 称为 主串 ;P 称为 模式串 。下面的图片展示了一个例子。

如何更好地理解和掌握 KMP 算法?

主串是莎翁那句著名的 “to be or not to be”,这里删去了空格。“no” 这个模式串的匹配结果是“出现了一次,从S[6]开始”;“ob”这个模式串的匹配结果是“出现了两次,分别从s[1]、s[10]开始”。按惯例,主串和模式串都以0开始编号。

字符串匹配是一个非常频繁的任务。例如,今有一份名单,你急切地想知道自己在不在名单上;又如,假设你拿到了一份文献,你希望快速地找到某个关键字(keyword)所在的章节……凡此种种,不胜枚举。

我们先从最朴素的Brute-Force算法开始讲起。

Brute-Force

顾名思义,Brute-Force是一个纯暴力算法。说句题外话,我怀疑,“暴力”一词在算法领域表示“穷举、极低效率的实现”,可能就是源于这个英文词。

首先,我们应该如何实现两个字符串 A,B 的比较?所谓 字符串比较 ,就是问“两个字符串是否相等”。最朴素的思想,就是从前往后逐字符比较,一旦遇到不相同的字符,就返回False;如果两个字符串都结束了,仍然没有出现不对应的字符,则返回True。实现如下:

如何更好地理解和掌握 KMP 算法?

既然我们可以知道“两个字符串是否相等”,那么最朴素的字符串匹配算法 Brute-Force 就呼之欲出了——

  • 枚举 i = 0, 1, 2 … , len(S)-len(P)
  • 将 S[i : i+len(P)] 与 P 作比较。如果一致,则找到了一个匹配。

现在我们来模拟 Brute-Force 算法,对主串 “AAAAAABC” 和模式串 “AAAB” 做匹配:

如何更好地理解和掌握 KMP 算法?

这是一个清晰明了的算法,实现也极其简单。下面给出Python和C++的实现:

如何更好地理解和掌握 KMP 算法?
如何更好地理解和掌握 KMP 算法?

我们成功实现了 Brute-Force 算法。现在,我们需要对它的时间复杂度做一点讨论。按照惯例,记 n = |S| 为串 S 的长度,m = |P| 为串 P 的长度。

考虑“字符串比较”这个小任务的复杂度。最坏情况发生在:两个字符串唯一的差别在最后一个字符。这种情况下,字符串比较必须走完整个字符串,才能给出结果,因此复杂度是 O(len) 的。

由此,不难想到 Brute-Force 算法所面对的最坏情况:主串形如“AAAAAAAAAAA…B”,而模式串形如“AAAAA…B”。每次字符串比较都需要付出 |P| 次字符比较的代价,总共需要比较 |S| – |P| + 1次,因此总时间复杂度是

如何更好地理解和掌握 KMP 算法?

. 考虑到主串一般比模式串长很多,故 Brute-Force 的复杂度是

如何更好地理解和掌握 KMP 算法?

,也就是 O(nm)的。这太慢了!

Brute-Force的改进思路

经过刚刚的分析,您已经看到,Brute-Force 慢得像爬一样。它最坏的情况如下图所示:

如何更好地理解和掌握 KMP 算法?

我们很难降低字符串比较的复杂度(因为比较两个字符串,真的只能逐个比较字符)。因此,我们考虑 降低比较的趟数 。如果比较的趟数能降到足够低,那么总的复杂度也将会下降很多。要优化一个算法,首先要回答的问题是“我手上有什么信息?”我们手上的信息是否足够、是否有效,决定了我们能把算法优化到何种程度。请记住: 尽可能利用残余的信息,是KMP算法的思想所在

在 Brute-Force 中,如果从 S[i] 开始的那一趟比较失败了,算法会直接开始尝试从 S[i+1] 开始比较。这种行为,属于典型的“没有从之前的错误中学到东西”。我们应当注意到,一次失败的匹配,会给我们提供宝贵的信息——如果 S[i : i+len(P)] 与 P 的匹配是在第 r 个位置失败的,那么从 S[i] 开始的 (r-1) 个连续字符,一定与 P 的前 (r-1) 个字符一模一样!

如何更好地理解和掌握 KMP 算法?

需要实现的任务是“字符串匹配”,而每一次失败都会给我们换来一些信息——能告诉我们,主串的某一个子串等于模式串的某一个前缀。但是这又有什么用呢?

跳过不可能成功的字符串比较

有些趟字符串比较是有可能会成功的;有些则毫无可能。我们刚刚提到过,优化 Brute-Force 的路线是“尽量减少比较的趟数”,而如果我们跳过那些 绝不可能成功的 字符串比较,则可以希望复杂度降低到能接受的范围。

那么,哪些字符串比较是不可能成功的?来看一个例子。已知信息如下:

  • 模式串 P = “abcabd”.
  • 和主串从S[0]开始匹配时,在 P[5] 处失配。
如何更好地理解和掌握 KMP 算法?

首先,利用上一节的结论。既然是在 P[5] 失配的,那么说明 S[0:5] 等于 P[0:5],即”abcab”. 现在我们来考虑:从 S[1]、S[2]、S[3] 开始的匹配尝试,有没有可能成功?

从 S[1] 开始肯定没办法成功,因为 S[1] = P[1] = ‘b’,和 P[0] 并不相等。从 S[2] 开始也是没戏的,因为 S[2] = P[2] = ‘c’,并不等于P[0]. 但是从 S[3] 开始是有可能成功的——至少按照已知的信息,我们推不出矛盾。

如何更好地理解和掌握 KMP 算法?

带着“跳过不可能成功的尝试”的思想,我们来看next数组。

next数组

next数组是对于模式串而言的。P 的 next 数组定义为:next[i] 表示 P[0] ~ P[i] 这一个子串,使得 前k个字符 恰等于 后k个字符 的最大的k. 特别地,k不能取i+1(因为这个子串一共才 i+1 个字符,自己肯定与自己相等,就没有意义了)。

如何更好地理解和掌握 KMP 算法?

上图给出了一个例子。P=”abcabd”时,next[4]=2,这是因为P[0] ~ P[4] 这个子串是”abcab”,前两个字符与后两个字符相等,因此next[4]取2. 而next[5]=0,是因为”abcabd”找不到前缀与后缀相同,因此只能取0.

如果把模式串视为一把标尺,在主串上移动,那么 Brute-Force 就是每次失配之后只右移一位;改进算法则是 每次失配之后,移很多位 ,跳过那些不可能匹配成功的位置。但是该如何确定要移多少位呢?

如何更好地理解和掌握 KMP 算法?

在 S[0] 尝试匹配,失配于 S[3] <=> P[3] 之后,我们直接把模式串往右移了两位,让 S[3] 对准 P[1]. 接着继续匹配,适配于 S[8] <=> P[6], 接下来我们把 P 往右平移了三位,把 S[8] 对准 P[3]. 此后继续匹配直到成功。

我们应该如何移动这把标尺? 很明显,如图中蓝色箭头所示,旧的后缀要与新的前缀一致 (如果不一致,那就肯定没法匹配上了)!

回忆next数组的性质:P[0] 到 P[i] 这一段子串中,前next[i]个字符与后next[i]个字符一模一样。既然如此,如果失配在 P[r], 那么P[0]~P[r-1]这一段里面, 前next[r-1]个字符恰好和后next[r-1]个字符相等 ——也就是说,我们可以拿长度为 next[r-1] 的那一段前缀,来顶替当前后缀的位置,让匹配继续下去!

您可以验证一下上面的匹配例子:P[3]失配后,把P[next[3-1]]也就是P[1]对准了主串刚刚失配的那一位;P[6]失配后,把P[next[6-1]]也就是P[3]对准了主串刚刚失配的那一位。

如何更好地理解和掌握 KMP 算法?

如上图所示,绿色部分是成功匹配,适配于红色部分。深绿色手绘线条标出了相等的前缀和后缀, 其长度为next[右端] . 由于手绘线条部分的字符是一样的,所以直接把前面那条移到后面那条的位置。因此说, next数组为我们如何移动标尺提供了依据 。接下来,我们实现这个优化的算法。

利用next数组进行匹配

了解了利用next数组加速字符串匹配的原理,我们接下来代码实现之。分为两个部分:建立next数组、利用next数组进行匹配。

首先是建立next数组。我们暂且用最朴素的做法,以后再回来优化:

如何更好地理解和掌握 KMP 算法?

如上图代码所示,直接根据next数组的定义来建立next数组。不难发现它的复杂度是

如何更好地理解和掌握 KMP 算法?

的。

接下来,实现利用next数组加速字符串匹配。代码如下:

如何更好地理解和掌握 KMP 算法?

如何分析这个字符串匹配的复杂度呢?乍一看,pos值可能不停地变成next[pos-1],代价会很高;但我们使用摊还分析,显然pos值一共顶多自增len(S)次,因此pos值减少的次数不会高于len(S)次。由此,复杂度是可以接受的,不难分析出整个匹配算法的时间复杂度:O(n+m).

快速求next数组

终于来到了我们最后一个问题——如何快速构建next数组。

首先说一句:快速构建next数组,是KMP算法的精髓所在,核心思想是“ P自己与自己做匹配 ”。

为什么这样说呢?回顾next数组的完整定义:

  • 定义 “k-前缀” 为一个字符串的前 k 个字符; “k-后缀” 为一个字符串的后 k 个字符。k 必须小于字符串长度。
  • next[x] 定义为: P[0]~P[x] 这一段字符串,使得 k-前缀恰等于k-后缀 的最大的k.

这个定义中,不知不觉地就包含了一个匹配——前缀和后缀相等。接下来,我们考虑采用递推的方式求出next数组。如果next[0], next[1], … next[x-1]均已知,那么如何求出 next[x] 呢?

来分情况讨论。首先,已经知道了 next[x-1](以下记为now),如果 P[x] 与 P[now] 一样,那最长相等前后缀的长度就可以扩展一位,很明显 next[x] = now + 1. 图示如下。

如何更好地理解和掌握 KMP 算法?

刚刚解决了 P[x] = P[now] 的情况。那如果 P[x] 与 P[now] 不一样,又该怎么办?

如何更好地理解和掌握 KMP 算法?

如图。长度为 now 的子串 A 和子串 B 是 P[0]~P[x-1] 中最长的公共前后缀。可惜 A 右边的字符和 B 右边的那个字符不相等,next[x]不能改成 now+1 了。因此,我们应该 缩短这个now ,把它改成小一点的值,再来试试 P[x] 是否等于 P[now].

now该缩小到多少呢?显然,我们不想让now缩小太多。因此我们决定,在保持“P[0]~P[x-1]的now-前缀仍然等于now-后缀”的前提下,让这个新的now尽可能大一点。 P[0]~P[x-1] 的公共前后缀,前缀一定落在串A里面、后缀一定落在串B里面。换句话讲:接下来now应该改成:使得 A的k-前缀 等于 B的k-后缀 的最大的k.

您应该已经注意到了一个非常强的性质—— 串A和串B是相同的 !B的后缀等于A的后缀!因此,使得A的k-前缀等于B的k-后缀的最大的k,其实就是串A的最长公共前后缀的长度 —— next[now-1]!

如何更好地理解和掌握 KMP 算法?

来看上面的例子。当P[now]与P[x]不相等的时候,我们需要缩小now——把now变成next[now-1],直到P[now]=P[x]为止。P[now]=P[x]时,就可以直接向右扩展了。

代码实现如下:

如何更好地理解和掌握 KMP 算法?

应用摊还分析,不难证明构建next数组的时间复杂度是O(m)的。至此,我们以O(n+m)的时间复杂度,实现了构建next数组、利用next数组进行字符串匹配。

以上就是KMP算法。它于1977年被提出,全称 Knuth–Morris–Pratt 算法。让我们记住前辈们的名字:Donald Knuth(K),James H. Morris(M),Vaughan Pratt(P).

希望本文对你有帮助。 本文在我博客的url是 https://ruanx.pw/kmp/ , 以后可能会更新。

最后附上洛谷P3375【模板】KMP字符串匹配 的Python和Java版代码:

如何更好地理解和掌握 KMP 算法?
如何更好地理解和掌握 KMP 算法?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

(0)
上一篇 2022年5月9日 下午5:57
下一篇 2022年5月9日 下午5:59

相关推荐

  • 阿里巴巴合伙人名单公布,阿里合伙人都有什么权利

    阿里巴巴日前向美国SEC递交年报,年报显示,截至2019年6月3日,阿里巴巴董事局主席马云持股为6.2%,为第三大股东;阿里巴巴董事局副主席蔡崇信持股为2.2%,为第四大股东。 截至2019年6月3日,阿里股权结构 截至2019年6月3日,软银持股为25.9%,依然为第一大股东;Altaba持股为9.4%,为第二大股东。 截至2018年7月18日,阿里股权结构 而截至2018年7月18日,马云持股…

    2022年8月6日
    960
  • 扫描软件下载(扫描软件app推荐)

    提起图片扫描识别为文字(OCR)的APP,最大名鼎鼎的就是扫描全能王,这个APP是非常好用的,但是高级功能都要收费,而且有广告。 今天给大家介绍一款同类APP,不仅免费无广告,不限制使用次数,功能还更强大,APP名字叫洋果扫描王。 还支持拍图识字、人脸测试、文字识别、花草识别、动物识别、车型识别、证件识别等非常有趣的功能。 扩展功能中还支持了PDF转图片、EXCEL转PDF、图片转PDF等功能。 …

    2022年5月13日
    770
  • 微信聊天文件怎么恢复,两个方法帮你快速解决

    大家都知道,现在越来越多的小伙伴喜欢使用微信,不管是和好友聊天、分享生活乐趣 还是交易支付,我们已经是越来越离不开微信了。有很多人还会用微信来接收工作、学习上一些比较重要的文件,但是当我们习惯性清理完微信内存以后就会发现那些重要文件都打不开了,这就有点麻烦了。 别担心,今天小编将分享几个非常简单的微信文件找回方法,帮大家轻松解决这样的问题~ 一、手机文件夹找回 1.文件管理器 我们在微信中查看文件…

    2022年9月21日
    680
  • 市场营销师应该具备什么能力,市场营销技巧分享

    三、市场营销的含义 市场营销的含义不是固定不变的,它随着企业市场营销实践的发展而发展。 1985年,美国市场营销协会(AMA)将市场营销定义为:市场营销是关于构思、货物和服务的设计、定价、促销和分销的规划与实施过程,目的是创造能实现个人和组织目标的交换。在交换双方中,如果一方比另一方更主动、更积极地寻求交换,则前者称为市场营销者,后者称为潜在顾客。所谓市场营销者,是指希望从别人那里取得资源并愿意以…

    2022年5月19日
    720
  • 机械硬盘哪个牌子好(最稳定的机械硬盘品牌)

    从小就有喜欢鼓捣数码电子类产品的“毛病”,好的弄坏,坏的又弄好;乐趣就在折腾中细细品味;2006年有了自己的第一台电脑,应该是TCL的品牌,陪我度过了大学时光,虽然现在看来配置很低,也没有液晶显示器,但是也没少折腾,坏了修,好了整坏…最后一次组装电脑应该是2016年了,按照自己的喜好组装了一台小机箱电脑海盗船380T;这台机器组装完毕之后留下了一个“空缺”和一个“痛点”; 痛点就是固态升级,前…

    2022年10月26日
    730
  • 灰色裤子怎么搭配上衣,看完这些让你秒变时尚达人

    灰色是在白色里面加了杂质,而白色又是最浅的颜色,所以一般的有彩色都能搭配,只是在色彩的饱和度,亮度搭配出来有效果的差异。 黑白灰之间的无彩色,都能相互搭配。一起来看一下相关搭配吧。 黑色上衣搭配浅灰色裤子 ​ 浅灰色的直筒裤能够修饰腿型不直的缺点,高腰线也能够显高,整体一套显得比较中性,高级。 再来看一组黑色上衣的搭配 ​ 想要女人味一点,可以搭配图中这种镂空衫,可以中和掉干练,又能够凸显小女人味…

    2022年9月10日
    1300
  • 电竞显示器品牌排行榜(电竞显示器品牌排行榜前十名)

    显示屏是电脑来说是它的输出终端,评判一款电脑好不好,除了其运行时的速度,显示屏的展现效果也是其重要的考量指标之一。那么电脑显示屏哪个牌子好呢?下面是十大显示屏品牌排行榜,一起来了解一下。 10.HKC显示器 HKC(惠科),其于2001年12月成立于深圳,一款国产品牌,价格十分便宜,主打便宜实惠,目前网吧用户和入门用户选择较多。为了降低一些预算,HKC做工不如AOC、三星、LG等知名品牌。 HKC…

    2022年10月26日
    1450
  • 产品3d效果图怎么制作用什么软件(手机上做3d建模的软件推荐)

    近年来,3D房屋建筑设计逐渐变得热门起来,过去的二维图纸,虽然大体上能让施工单位和业主了解房屋的外观和布局,但是其表现形式,不如三维直观,容易造成最终的建筑效果与业主的理想效果存在差距。 3D房屋建筑设计则正好能弥补二维设计带来的不足,不仅房屋整体结构布局一目了然,且能立体的展示二维图纸无法呈现的电气、暖通、管道和给排水等专业的模型。 3D房屋建筑设计虽然发展时间不长,但是市面上的设计软件却五花八…

    2022年5月11日
    850
  • 批量下载qq空间相册(QQ空间原图批量下载)

    经常使用QQ的朋友们都知道QQ相册可以用来保存和分享我们所拍摄的相片,但在长期的使用中我们不难发现,它似乎并没有为我们提供批量下载功能,那么我们要如何去批量下载QQ相册里面的照片呢? 今天,要给大家介绍的是这样一款能够实现QQ相册照片批量下载功能的工具——固乔电商图片助手,我们可以使用它轻松地下载我们QQ相册里的照片。而究竟要如何使用它来下载我们所需要的相册照片呢,请跟着小编继续往下看。 需要用到…

    2022年5月2日
    1670
  • 如何提高网站搜索排名,3个提升网页搜索排名的好方法

    企业做了网站,可是不知道如何来推广,不知道如何来优化网站,不知道如何来提高网站的搜索排名,就让小编来告诉您吧! 网站搜索排名 第一:要保证网站内容的定期更新,更新内容是要做到以下几点: 1:网站的产品类别最好加上城市名。 2:网站的类别最好和关键词有关系。 3:网站里产品信息的标题也最好加上城市名。 4:网站里产品信息的内容里也要有相关的关键词。 在这里给大家举个例子: 比如有一家北京的企业,主营…

    2022年7月8日
    5510

发表回复

登录后才能评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信