点击上方“Python爬虫与数据挖掘”,进行关注

回复“书籍”即可获赠Python从入门到进阶共10本电子书

云母屏风烛影深,长河渐落晓星沉。

今天是算法数据结构专题的第29篇文章,我们来聊一个新的字符串匹配算法——KMP。

KMP这个名字不是视频播放器,更不是看毛片,它其实是由Knuth、Morris、Pratt这三个大牛名字的合称。老外很喜欢用人名来命名算法或者是定理,数学里就有一堆,什么高斯定理、欧拉函数什么的。但是中国人更倾向于从表意上来给一个概念命名,比如勾股定理、同余定理等等。之前觉得用人名命名很洋气,作者可以青史留名,后来想想这也是英文表意能力不足,很难用表意的方式起名的体现。

扯远了,我们回到正题。

应用场景

在计算机领域当中字符串匹配其实是一个非常常见的问题,我们使用它的场景也多到不可计数。比如在一个已经打开的页面当中搜索关键词,再比如说git里面的代码变动的记录,以及论文的查重等等。在这些问题当中有些情况可能还好,比如说我们搜索一个关键词,因为关键词并不长,我们暴力枚举也不会特别耗时。但是在有些问题当中明显暴力匹配是无法胜任的,比如论文查重。一篇论文动辄上千词,要和库中的上万篇文章进行查重扫描,这当中的工作量可想而知。如果是暴力枚举算法那查重显然会查到天荒地老。

所以早期的时候字符串匹配是一个难题,既然是难题那么显然就会有很多人来研究,也因此出了很多成果,很多大牛发表了字符串匹配的算法,其中KMP算法由于效率很高、实现复杂度低被应用得最广。到这里,我们就知道KMP算法是用来字符串匹配的。

比方说我们有两个字符串,A串是:I hate learning English. B串是hate learning,很明显B串是A串的字符串。如果我们暴力枚举来判断的话,我们需要遍历A串当中的每一个起始位置是否能够完成匹配,那么复杂度显然是。通过KMP算法,我们可以在的时间内做到这点。

著名的大佬matrix67在KMP算法的介绍博客当中有一句著名的骚话,当你有一个喜欢的MM,你可以委婉地问她:“假如你要向你喜欢的人表白的话,我的名字是你的告白语中的子串吗?”

Next数组

KMP算法的核心精髓只有一个就是Next数组,但是这个概念并不太容易理解,很多人学KMP放弃就是折戟在了Next这个数组上。

我们先把Next数组是怎么来的放在一边,先来看下Next数组是用来干嘛的,它起作用的原理是什么,最后再来讨论Next数组怎么来的问题。根据我的理解,Next数组其实就是一个中途开始的机会,也就是当我们在枚举匹配的时候,发现了不匹配的情况,我们不是从头开始,而是从一个最大可能的中间结果开始。

我们来看个例子:


上图中上面的是A串,下面的是B串,我们在匹配的过程当中发现B串的前面几位都匹配上了,而在最后一位匹配失败。按照常规的做法,我们应该是移动到下一个位置从头开始匹配。但是这是非常浪费的,因为我们观察下可以发现失败位置的ABC和B串开头的ABC是可以构成匹配的


我们之前失败的时候判断的是以C结尾的ABCDABC和B串的匹配,在这一次匹配失败之后,我们可以继续尝试匹配其他以C结尾的前缀串,比如ABC。这样我们就可以从中间状态开始,而节省了许多次不必要的枚举。但问题就来了,这个中间结果是怎么来的呢,我们怎么知道当下失败了上一个可行的中间结果是哪一个?

对,没有错,前面说到的Next数组就是用来存储中间结果的。所以Next可以理解成下一次机会的意思,这样就好理解了。由于我们是在A串当中寻找B串,所以这个Next数组应该是针对B的,记录B中每一个位置如果匹配失败,它的前面一个可行的中间状态是哪一个。


我们先写出来B的Next数组,等会再去研究它是怎么得到的。为了简化编码,我们假设字符串是从1位置开始的,所以我们在0的位置添加一个$符号作为占位符。对于大部分情况都是没有重来的机会的,失败了直接归零。而其中的A和B两个位置是有重来机会的,因为B的前缀当中出现了A和AB。所以如果在匹配ABD的时候失败了,我们还可以从AB处再次开始尝试匹配ABC。

算法原理

我们想象一根指针指向了B数组当中接下来要匹配的位置,如果匹配失败了,它就会跳转到Next数组当中记录的位置去,匹配成功了我们就向后移动一位。在有了Next数组之后,我们写出代码来真的很容易了:

def kmp(var_str, template_str):# var_str即A串# template_str模式串即B串# 我们在两个字符串前加上了占位符var_str = '$' + var_strtemplate_str = '$' + template_strnext = generate_next(template_str)n, m = len(var_str), len(template_str)# head指向要匹配的位置的前一位head = 0for i in range(1, n):# 由于next数组很长,可能失败多次# 直到head+1的位置能匹配上或者head等于0while head > 0 and template_str[head+1] != var_str[i]:head = next[head]# 匹配上了则head变长一位if template_str[head+1] == var_str[i]:head += 1# 如果head长度等于B串了,则表示匹配成功if head == m - 1:return Truereturn False

对于A串中的每一个位置来说,我们都在B串当中遍历了每一个有可能构成匹配的前缀。所以说这个算法是可行的,一定可以获得解。另外一个问题是复杂度的问题,为什么我们用了两重循环,但仍然是的算法呢?

其实很简单,因为while循环只会让head减小,而不会让head增加。head增加是在for循环里执行的,也就是说head最多增加n次。那么对应的while循环也就最多执行n次,因为head是非负的。所以while循环在整个for循环执行的过程当中最多执行了n次,整体执行的次数仍然是级别的而不是级,当然是线性的算法。

求解Next

到这里,问题只剩下了一个,就是这个Next怎么来呢?

其实我们在之前讲Next数组的使用的时候已经泄露天机了,我们再来看下上图,不知道大家能感觉到什么。


后面一个A的Next值是1,也就是第一个A的下标,后面一个B的Next值是2,也就是第一个B的下标。换句话说第二个A能够和位置1的A匹配,后面的AB能和前缀的AB匹配。也就是说Next数组其实就是B数组自己和自己匹配的结果,我们在一开始的时候将整个Next数组全部置为0,然后依次递推迭代出所有的Next的值。

我们在求解Next[i]的时候我们可以利用上Next[i-1]的值,因为Next[i-1]存储的是能够与B[i-1]匹配的前缀的结尾位置。如果B[Next[i-1]+1]等于B[i],那么说明Next[i] = Next[i-1] + 1。如果不等的话,我们可以用while循环来寻找能够匹配上的前缀。也就是说这是一个递推的过程,不过要注意一点我们计算Next数组要从2开始,因为对于1来说,Next[1]一定等于0。

def generate_next(var_str):n = len(var_str)next = [0 for _ in range(n)]for i in range(2, n):# 用next[i-1]作为开始寻找能够匹配上的最长next[i]head = next[i-1]while head > 0 and var_str[head+1] != var_str[i]:head = next[head]# 如果匹配上了,head+1if var_str[head+1] == var_str[i]:head = head + 1# 记录下来next[i] = headreturn next

总结

到这里,我们关于KMP算法的介绍就结束了,不知道大家看完之后感受如何,是不是有点蒙圈呢?

其实蒙圈是正常的,我第一次学的时候足足看了好几遍才算是看明白。这毕竟是一个比较巧妙的算法,想要通过阅读一篇文章就完全学会还是比较困难的,最好的还是亲自动手实现一下试试。KMP算法我最大的感受就是如果你把整个算法的逻辑都串起来了,那么即使自己从头到尾推导一遍难度也不是很大(我就在面试当中推导过一次)。如果你没能把逻辑串起来,那么觉得难理解看不懂是正常的,你可能需要再读一遍或者是寻找一些其他的资料查漏补缺。

------------------- End -------------------

往期精彩文章推荐:

  • 手把手教你使用Flask搭建ES搜索引擎(实战篇)

  • 简述Python、Anaconda、virtualenv和Miniconda之间的区别

  • 【进阶篇】Python+Go——带大家一起另寻途径提高计算性能

欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持

想加入Python学习群请在后台回复【入群

万水千山总是情,点个【在看】行不行

/今日留言主题/

随便说一两句吧~~

算法数据结构 | 只要30行代码,实现快速匹配字符串的KMP算法相关推荐

  1. js实现kmp算法_「leetcode」459.重复的子字符串:KMP算法还能干这个!

    不瞒你说,重复子串问题,KMP很拿手 题目459.重复的子字符串 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成.给定的字符串只含有小写英文字母,并且长度不超过10000. 示例 1: ...

  2. 匹配字符串之——KMP算法深入理解

    KMP算法的用途 一般在字符串匹配的时候,我们通常想到的就是使用KMP算法来处理. KMP的使用,在网上有很多实例,但是讲得很让人容易接受的不是很多,但总感觉那层窗户纸没有捅破,这里我结合前人的知识, ...

  3. Python程序员30行代码素描表白!网友:花里胡哨

    总有人说程序员不够浪漫!其实我们只是没时间而已,等我们有时间了,还有普通人什么事儿?最近就有一个小伙伴上热搜了! 原来他用Python给可爱的女朋友画了一幅素描!不到30行代码,一起来学学给她一个惊喜 ...

  4. 30行代码,带你分分钟创建神经网络!(附工具教程)

    来源:大数据文摘 作者:Per Haiald Borgen 本文长度为1612字,建议阅读3分钟 本文为你介绍如何使用Synaptic.js创建和训练神经网络. 本文含大量代码,如需原文请从文末来源链 ...

  5. 代码补全快餐教程(1) - 30行代码见证奇迹

    代码补全快餐教程(1) - 30行代码见证奇迹 下面是我用30多行代码,包含了很多空行和注释的代码写成的代码补全模型.我们先看看效果吧. 补全效果案例 先来看个比较普通的(Python, Keras) ...

  6. 实时观看:如何用30行代码炸毁27吨重的发电机

     聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士团队 上周早些时候,美国司法部公开了一份针对黑客组织 Sandworm 的起诉书.为俄罗斯军事情报机构格鲁乌 (GRU) 效劳的六名黑客被控 ...

  7. 本周AI热点回顾:30行代码,让27吨发电机原地爆炸;Photoshop把AI论文demo打包实现了;2块钱就能买上千张人脸照片

    点击左上方蓝字关注我们 01 Photoshop把AI论文demo打包实现了:照片上色.改年龄.换表情只需要点点鼠标 这两年,我们从很多论文中看到过一些令人惊艳的 demo,比如老照片自动上色.低画质 ...

  8. 拆分工作簿为多个文件_掌握这几行代码,快速拆分Excel工作簿(内含源码)

    工作中,常常会根据工作表的某一内容来拆分工作博并按照指定的命名新建工作簿,且放入指定的路径文件夹下面.今天就拿昨天的例子,来分享一下如何通过几行代码,快速的拆分工作簿 源数据: 目标工作簿及内容: 问 ...

  9. vb.net读取excel并写入dgv_读取PDF中的表格写入EXCEL?30行代码搞定

    办公自动化系列+1 现在,各类数据分析的书籍,都可以在网上找到PDF版本: 同时,百度文库.各类数据统计文库.行业研究等众多论文报告,是通过PDF的形式去展示输出的: 但是,令人都头疼的是,各类数据分 ...

  10. thymealf如何实现传单个变量给html_梦回2013,看尤大vue的第一行代码,如何用30行代码实现vue(超简洁,适合初学者)...

    非非非标题党,干货预警!!! 介绍 大家好,我是清池交友 app 开发日记,记录清池交友 app 开发中学习过程和踩坑日记,伪全栈[1] 技术栈:前端 js,vue,uniapp,后端 java 尤大 ...

最新文章

  1. 洛谷P2503 [HAOI2006]均分数据(模拟退火)
  2. MySQL Server系统架构
  3. opencv 灰度图
  4. Could not resolve the package 'english_words' in 'package:english_words/english_words.dart'
  5. ARP、Tracert、Route 与 NBTStat命令详解
  6. behavior php,YII2框架中behavior行为的理解与使用方法示例
  7. 简 易 版 的 进 程 池 模 型 学 习
  8. Python实现动态图的解析、合成与倒放
  9. 基于android的高仿抖音,Android仿抖音列表效果
  10. 实施和开发哪个前景好_「深圳app开发」app模板开发和app定制开发哪个好呢?
  11. 你可能不知道的10条SQL技巧,涨知识了!
  12. 最简单的视音频播放示例8:DirectSound播放PCM
  13. Nacos教程_4 配置讲解
  14. iis swagger 部署_AspNet Core Api Restful +Swagger 发布IIS 实现微服务之旅 (二)
  15. 如何复制百度文库中需要收费的文字
  16. oppo自带计算机版本,OPPO手机助手
  17. 后台管理系统架构成型
  18. 计算机两个活动分区,硬盘无法设置活动分区,硬盘只能有一个活动分区吗
  19. 如何截图一张完整的表_wps2016表格太长怎么截图打印?
  20. 移动端适配之一:到底什么是像素

热门文章

  1. PR免费转场 动态图形转场PR模板MOGRT免费下载
  2. 谷歌浏览器黑色主题设置
  3. 前端实现很哇塞的浏览器端扫码功能
  4. 如何使用视频剪辑软件将qsv格式视频转换为MP4格式
  5. 萤石云平台接入_新手上路 老司机教你配置萤石云
  6. unity物体四种移动方法总结
  7. 三相逆变器双pi控制器参数如何调节_SPMSM控制:传统PI电流环参数的整定
  8. 一条命令关闭极域电子教室学生端
  9. Insyde uefi 隐藏设置_文颐法语:iOS 14安装后发现的9个隐藏的iPhone功能
  10. spring aop切面执行顺序