作者 | 落阳学编程

责编 | 王晓曼

出品 | CSDN 博客

前言

近日被朋友问到了字符串匹配算法,让我想起了大二上学期在一次校级编程竞赛中我碰到同样的问题时,为自己写出了暴力匹配算法而沾沾自喜的经历。

现在想来,着实有点羞愧,于是埋头去学习了一下 KMP 算法,为了让自己不至于那么快忘记,也希望小伙伴们能从我的理解中收获一点自己的感悟!

文章带有精心雕琢的动画以便理解。

分析

我们首先来分析一下暴力算法,为鲜花的诞生献上绿叶!

以下文中统一将需要被匹配的字符串(长的那段)称为待匹配串,把用来匹配的字符串(短的那段)称为模式串。

暴力匹配算法的思路很简单,就是每一次都首先将待匹配串和模式串的首字母对齐,然后比对是否相同,若相同则继续比对两个串的下一个位置,如果不相同的话就将模式串向右移动一位,然后再重新开始从头匹配,就像下面这样:

从上面的动画我们可以直观的看出来,下面的模式串在匹配失败之后都只会移动一格,傻里傻气的,这就导致它的时间复杂度是M∗N M*NM∗N,其中M是模式串的长度,N是待匹配串的长度。

对于这个时间复杂度,我不满意!它太傻了,不符合我聪明睿智的气质!

那就来分析一下为何它这么傻。我们可以看到,在第一次匹配失败的时候,我们肯定希望它向右移动至少两格,因为模式串的第一格和第三格都为a,既然第三格已经匹配成功了,那么把第一格对上第三格匹配的位置,那么无疑肯定也是可以成功的,我们的算法本该知道并且利用这一点的!但是它没有,它太傻了。

嗯,这么一说,好像是感觉应该是要把它向着动态规划的方向改(即利用已有信息为下一步提供便利)。

PS:字符串问题百分之八十以上都可以使用动态规划思想达到较低的时间复杂度。

核心问题

我们大都听过一句老话:人啊,贵在有自知之明。

同时我们肯定也听别人说过:人只有深刻的认识了自己,才能找对位置,迅速地向目标前进!

这两句话用在KMP算法中再合适不过了!

KMP算法的核心便在于,模式串对自己的自我认知!

想一想,我们人对自己的认知是如何的:男,19岁,阳光帅气聪明机智,这些自我认知都存放在我的脑袋里面。

那么,模式串对自己的认知应该存放在哪呢?

对,就是next数组里面!字符串没有大脑,所以它需要额外的空间来存储它对自己的认知并借此作出高效准确的判断。

那么字符串对自己的认知是怎样的呢?其实很容易理解,就是知道自己身上哪些地方是相同的,这样的话在匹配失败之后就能迅速找准下次开始的点。这里是不是有点模糊了?图来!

以上就是KMP算法的动画,如果觉得动画稍微有点快的话可以多观看几次,在这个动画里我还没有放出next数组的部分,只是用拟人化的手法展现出来。希望大家能够理解,为什么第一次匹配失败可以直接移动两格。

是因为模式串中第三格的a,它知道在第一格有与自己相同的字符,并且把这个信息告诉下一格的字符,让它在匹配失败之后直接把第一格的a移动到它的那个位置上去。

来看看模式串与其对应的next自我认识数组吧。

不要去在意next数组的第一个为什么是-1,这是为了代码写的方便,暂且就给它当成0。

在动画中,当一个字符发出“直接移动”的语句的时候,其实是告诉后一个字符,如果你匹配失败了的话,就直接移动,同时后一个字符对应的next数组值为0,当后一个字符匹配失败了,就移动模式串的长度-这个匹配失败的字符对应的next值个长度。

从第四个字符(i=3)起,它们都在不断告诉后面一个字符:“将i=0移动到i=3的位置”,这句话对于i=4的字符来说,是移动4-1格,对于i=5的字符来说,是移动5-2格,对于i=6的字符来说,是移动6-3格:后面那个减数恰好就是这个字符对应的next数组的值!

因为模式串足够了解自己,所以它能够在匹配失败的时候不用回退,不用每次只移动一格,而是跟随着待匹配串一起移动。待匹配字符串的指针从未回退过,以线性的速度向前一步步越进。

最终:KMP算法的时间复杂度是M+N M+NM+N

这里我们不禁发出了感叹!原来认识自己真的这么重要啊!

接下来是求出给定模式串的next数组:

Python3代码奉上:

def get_next_lst(ss: str) -> list:length = len(ss)next_lst = [0 for _ in range(length)]next_lst[0] = -1i = 0j = -1while i < length - 1:if j == -1 or ss[i] == ss[j]:i += 1j += 1next_lst[i] = jelse:j = next_lst[j]return next_lst

这段代码最难理解的就是j=next_lst[j]这句话,其实这句话也是动态规划的一个思想,看我为你剖析一下。

已知蓝色区域相等且长度都为len,那么很明显,next[i] == len,若此时模式串pattern[i] != pattern[j](两个灰色区域不相等)。那么看下图:

若此时next[j] == len(粉色部分)那么S1==S2,又因为next[i] == next[j],所以S1==S3 且 S3 == S4,则可以推出S1 == S4,这样我们就利用前面所获得的信息,推出了S1 == S4这个信息,然后将J移动到S1后一格,只要再次比较patter[i] 与 patter[j]的相等情况,就可以得出next[i+1]的值。这里因为i始终向后移动,所以也是线性时间复杂度的算法。

ohhhhhhhhh~

到这里,大家就明白了为啥KMP算法的时间复杂度是M+N M+NM+N了。

KMP匹配字符串的完整代码附上!

class KMP():def __init__(self, ss: str) -> list:self.length = len(ss)self.next_lst = [0 for _ in range(self.length)]self.next_lst[0] = -1i = 0j = -1while i < self.length - 1:if j == -1 or ss[i] == ss[j]:i += 1j += 1self.next_lst[i] = jelse:j = self.next_lst[j]self.pattern = ssdef match_str(self, ss:str):ans_lst = []j = 0for i in range(len(ss)):if ss[i] != self.pattern[j]:j = self.next_lst[j] if self.next_lst[j] != -1 else 0if ss[i] == self.pattern[j]:j += 1if j == self.length:return i + 1 - self.lengthreturn -1tmp_kmp = KMP('iabc')
print(tmp_kmp.match_str('adosjfoiajsoifjasiofjoiasdjoiabc'))

版权声明:本文为CSDN博主「落阳学编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/luoyangIT/java/article/details/106041160

更多精彩推荐
☞中国第一代程序员潘爱民的 30 年程序人生
☞云上容器 ATT&CK 矩阵详解,阿里云助力企业容器化安全落地
☞秋名山老司机从上车到翻车的悲痛经历,带你深刻了解什么是 Spark on Hive | 原力计划
☞拯救渣画质,马赛克图秒变高清,杜克大学提出AI新算法
☞数据科学产业中哪些架构最热门?本文为你盘点了 5 款
☞行业大神支招!5 个方法让你的数字资产免受黑客祸害
你点的每个“在看”,我都认真当成了喜欢

开玩笑呢?学习KMP算法能改变自我认知? | 原力计划相关推荐

  1. 查漏补缺!这份 VUE 学习知识总结请注意查收! | 原力计划

    作者 | 新鲜的橘子 责编 | 王晓曼 出品 | CSDN博客 VUE实例的创建 该实例中的内容将和和下面的例子相对应: var vm = new Vue({el:'',data:{msg:" ...

  2. 一文读懂 KMP 算法 | 原力计划

    作者 | 落阳学编程 责编 | 王晓曼 出品 | CSDN 博客 前言 近日被朋友问到了字符串匹配算法,让我想起了大二上学期在一次校级编程竞赛中我碰到同样的问题时,为自己写出了暴力匹配算法而沾沾自喜的 ...

  3. 基于海康机器视觉算法平台的对位贴合项目个人理解 | CSDN原力计划

    扫码参与CSDN"原力计划" 作者 | 果汁分你一半哈哈 来源 | CSDN原力计划获奖作品 都说"纸上得来终觉浅,绝知此事要躬行",可惜咱没这条件呀,没项目咱 ...

  4. 不怕面试被问了!二叉树算法大盘点 | 原力计划

    作者 | BoCong-Deng 责编 | 伍杏玲 头图 | CSDN 下载自视觉中国 出品 | CSDN博客 树结构对于程序员来说应该不陌生,特别是二叉树,基本只要接触算法这一类的都一定会碰到的,所 ...

  5. 区块链共识算法总结 | 原力计划

    作者 | 日月ton光 责编 | 王晓曼 出品 | CSDN博客 常见共识算法介绍 在异步系统中,需要主机之间进行状态复制,以保证每个主机达成一致的状态共识.而在异步系统中,主机之间可能出现故障,因此 ...

  6. 史上最全排序算法总结 | 原力计划

    作者 | 铁猴 责编 | 屠敏 出品 | CSDN 博客  简介 本文对常见排序算法进行总结. 排序算法 冒泡排序 该算法比较简单,几乎所有语言涉及到算法时,都会涉及到冒泡算法. 算法思路: 比较相邻 ...

  7. 【C语言】算法学习·KMP算法

    KMP算法(全称Knuth-Morris-Pratt字符串查找算法,由三位发明者的姓氏命名)是可以在文本串s中快速查找模式串p的一种算法. 要想知道KMP算法是如何减少字符串查找的时间复杂度的,我们不 ...

  8. 学习KMP算法(详解)

    KMP算法是一个广泛应用于字符串查找与匹配的算法,特点就是计算速度快,例如在m长度的字符串中查找匹配长度为n的字符串,他的时间复杂度可以是m+n 对于字符串的查找与匹配,要是我们没有学习过数据结构或者 ...

  9. 深度学习专项课程精炼图笔记!必备收藏 | 原力计划

    作者 | DL&CV_study9 责编 | Elle 出品 | CSDN 博客 本文为人工智能学习笔记记录. 深度学习基础 深度学习基本概念 监督学习:所有输入数据都有确定的对应输出数据,在 ...

最新文章

  1. 如何提升深度学习性能?数据、算法、模型一个都不能少
  2. python英语翻译-python制作英语翻译小工具
  3. SD-WAN和虚拟专用网之间有什么区别?虚拟专用网会被替代吗?
  4. docker容器配置加速器
  5. 学习曾国藩,学做人做事学技术
  6. 大数据全栈式开发语言 – Python
  7. 最大连续子数组和 动态规划_每日LeetCode,乘积最大子数组
  8. mybatis的拦截器及分页机制
  9. ubuntu下的tomcat监控脚本
  10. 腾讯云数据库智能化海量运维的建设与实践
  11. IIS服务的命令行方式重启命令
  12. c语言对整形indexing,C语言运算符另类分类法
  13. SVN checckout 失败:Error: REPORT request on '/svn/signature/!svn/me' failed 的解决办法
  14. 设置Parallels Desktop中的Windows虚拟机使用Mac宿主机代理
  15. 实验题7.1 实现二分查找的算法
  16. 个人实名认证的几种方案
  17. 制程与良率,谁才是芯片厂商的竞赛底牌?
  18. 想知道CAD怎么转图片格式?跟我学会这几招就足够了
  19. mac 查看端口_交换机端口对应的mac地址与IP地址
  20. Android实现类似股票列表联动

热门文章

  1. layui 日期插件onchange事件失效的方法
  2. Linux下 FFmpeg 编译安装
  3. django-debug-toolbar 工具
  4. CUDA 计算pi (π)
  5. HTML/CSS基础知识总结
  6. LeetCode 3 Longest Substring Without Repeating Characters 区间,想法 难度:1
  7. VIJOS 1512SuperBrother打鼹鼠(二维BIT)
  8. Session 另一种用法,其实是一样的
  9. 虚拟机中CentOS 7 网络服务启动失败
  10. 特征工程之自动特征生成(自动特征衍生)工具Featuretools——深度特征合成