KMP算法由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。


文章目录

  • 前言
  • 问题类型
  • 算法讲解
    • 什么是KMP?
    • next数组
      • next数组的含义
      • next数组的求法
      • next数组代码实现
    • KMP的实现
    • KMP代码
  • 后记

前言

KMP算法在我看来是一种学起来比较困难但一学会之后就特别简明清晰的算法。
因为其本身的核心思想就并不是太难,所以在看本文的时候不要潜意识的认为算法很难
尽量用简单的方式去思考,不懂的地方其实多模拟几次就懂了。

再一个就是本人是一个萌新初学者,在一些解释方面可能会出现偏差和错误,请见谅。
我会用我的方式来让读者更快的理解KMP。


问题类型

相信读者在看我的文章时已经或多或少的了解过一些其他的KMP讲解了。
其实这个算法就是为了解决字符串匹配问题,简单来说就是寻找一个字符串里是否包含另一个字符串并可以返回其起始位置,
也可以用来寻找一个字符串中包含几个相同的子字符串,例:

str1=zyzyzyz
str2=zyz

则str1中包含了三个str2,起始位置分别是0,2,4。

KMP其实还有非常多的变式应用,只不过我都不会 这里就不再赘述,请读者自行查阅资料了解。

废话不多说,进入算法讲解部分。

算法讲解

什么是KMP?

KMP就是是一种改进的字符串匹配算法。
我们都知道,普通的暴力是一位一位的挪动字符串并逐位比较,
这样的时间复杂度会达到 O(nm)O(nm)O(nm),非常不利。

而KMP则是通过比较操作的简化来优化时间复杂度,不是一位一位的移动,
而是不后退的一段一段的移动,有读者想问:这不会出现遗漏的错误么?
这时候,就需要用到一个移动数组next,KMP算法的核心部分就是next数组的应用,使其时间复杂度大大降低,达到 O(n+m)O(n+m)O(n+m)

next数组

next数组的含义

首先要明白next数组是什么——
next[i]next[i]next[i] 表示一个字符串前 iii 个字符的最长的前缀和后缀相同的长度。
其中-1表示无最长的前缀和后缀,0表示1,1表示2,以此类推。
还是不明白?
举个例子:

str=ababc;
next[1]=-1;  /none
next[2]=-1;  /none
next[3]=0;   /a 是最长前缀和最长后缀相同的字符串
next[4]=1;   /ab 是最长前缀和最长后缀相同的字符串
next[5]=-1;  /none

两个注意点

  1. “kkkk” 的最长前缀和最长后缀相同的长度是 3,最长前缀和最长后缀相同的字符串为"kkk"
  2. 在“aba”中,ab和ba 不能算作是最长前缀和最长后缀相同!

next数组的求法

知道了next数组的含义之后,就来看看传说中“很难”的求法。
暴力?肯定不行,这样就失去了KMP的意义。
其实,next数组的求法也是 O(n+m)O(n+m)O(n+m).
下面我就用图加文字的方式解说next数组的求法。
以字符串"ababc"为例来演示:
kkk 代表当前字符串最长的前缀和后缀相同的长度为 kkk,初始化为-1,见上⭐
iii 代表当前字符串到了第 iii 位。

此时字符串“a”没有最长的前缀和后缀相同,next[0]=−1next[0]=-1next[0]=−1;

这时字符串"ab"仍没有最长的前缀和后缀相同,next[1]=−1next[1]=-1next[1]=−1;


此时 str[k+1]=str[i]str[k+1]=str[i]str[k+1]=str[i] (这里的K指的是-1,当这一句语句执行之后 kkk 才会 ++++++ 为 000)
字符串"aba"中 a是最长的前缀和后缀相同的部分。
所以 k++k++k++ ,至于为什么是这个判断条件,我在下一张图就详讲。

(首先注意:k在判断之后才会等于1,现在等于0!图中直接k=1是为了方便观看)
这个时候,“abab” 字符串中 ab 是最长的前缀和后缀相同的部分。

我们知道,前缀和后缀一定要完全相同才算数,
在现在匹配到的字符之前,我们已经匹配成功了 kkk 个字符,所以如果 k+1k+1k+1 和 iii 相等,
那么前缀和后缀必然可以匹配。(注意i是循环变量,自己就会 ++++++)
就算 k=−1k=-1k=−1 也没有关系,因为 k+1k+1k+1 作为起点( 000 是字符串起点)要与某个 iii 匹配之后才会继续匹配下去,
不然k不会改变,一直是起点。

(这时候要提醒一下,前缀和后缀一定要完全相同才算数!所以一个字符不同,就不能算是最长前缀和最长后缀相同)

重点来了,如果在匹配成功了一段字符串之后突然遇到两个字符匹配失败怎么办呢?

现在 str[k+1]=str[i]str[k+1]=str[i]str[k+1]=str[i] 匹配失败了!
k−−k--k−− 再匹配可以吗?
明显不行,因为 0→k−10\to {k-1}0→k−1 是已经匹配过的了,就算 k−1k-1k−1 可以与 iii 匹配,那么也会打乱阵型。
比如:

ababb,当3号位a不能和5号位b匹配时,就算2号位b能和5号位b匹配,总体来看这个字符串还是没有最长的前缀和后缀相同的部分

通过上面的推论,同理可得 1→k−11\to k-11→k−1 都不能与之匹配,那么谁可以尝试与之匹配并不会打乱阵型?
已经匹配成功的k个字符串最长的前缀和后缀相同的部分!
为什么呢?
因为只有让匹配成功的k个字符的 最长的前缀和后缀相同的部分 与之匹配才是符合要求的,其他的字符与之匹配也是白费力气,如同我上面的推论。

  • 重温一下概念:next[k]next[k]next[k] 表示一个字符串前 kkk 个字符的最长的前缀和后缀相同的长度。

k个字符串最长的前缀和后缀相同的部分怎么用程序语言表达?————next[k]next[k]next[k] !
所以直接让当前 k=next[k]k=next[k]k=next[k] 就可以节省非常多的时间匹配了。

next数组代码实现

void find_next()
{int k=-1;for(int i=1; i<=longmbs-1; i++){while(k>-1&&mbs[k+1]!=mbs[i])  //要不断往回找直到找不到为止(k=-1就没有最长的前缀和后缀相同的部分了)k=next[k];if(mbs[k+1]==mbs[i])k++;next[i]=k;    //将当前算出的结果赋给next数组}
}

当然你也可以不用k=-1去做,用字符数组直接k=0;

void find_next()
{int k=0;for(int i=2; i<=longmbs; i++){while(k>0&&mbs[k+1]!=mbs[i])k=next[k];if(mbs[k+1]==mbs[i])k++;next[i]=k;}
}

KMP的实现

如果你完全看懂了next数组的求法和 k=next[k]k=next[k]k=next[k] 的意义所在,KMP就不是问题了。
我们知道,next[k]next[k]next[k] 的实际用途是寻找当前已经匹配的k个字符的最长的前缀和后缀相同的部分。
而KMP刚好是用来寻找一个字符串里是否包含另一个字符串,所以——
可以利用next数组来实现KMP!

这里,我用了一个详细的图来解释KMP,相信你看了之后一定能懂!

KMP代码

void kmp()
{next[0]=-1;find_next();int k=-1;for(int i=0; i<=longys-1; i++){while(k>-1&&mbs[k+1]!=ys[i])k=next[k];    //位移的实现if(mbs[k+1]==ys[i])     //累计 匹配成功的字符 的个数k++;if(k==longmbs-1)      //找到了,从当前的k个字符的最长的前缀和后缀相同的部分继续找{ans++;k=next[k];}}
}

字符数组版

void kmp()
{find_next(); int k=0;for(int i=1; i<=longys; i++){while(k>0&&mbs[k+1]!=ys[i])k=next[k];if(mbs[k+1]==ys[i])k++;if(k==longmbs){ans++;k=next[k];}}
}

后记

完整KMP代码截这儿
有问题评论区留言,这里推几道题
KMP模板题
KMP题1
KMP题2

深入浅出系列之——KMP算法详解【吐血整理】相关推荐

  1. 【百度飞浆】YOLO系列目标检测算法详解

    YOLO系列目标检测算法详解 1 YOLO发展史 2 YOLO v3目标检测原理 3 PaddleDetection中YOLO v3模型介绍 4 YOLO v3配置演练 1 YOLO发展史 2 YOL ...

  2. 【百度飞浆】RCNN系列目标检测算法详解

    RCNN系列目标检测算法详解 目录 两阶段目标检测算法发展历程 R-CNN R-CNN网络结构 R-CNN网络效果 Fast R-CNN Fast R-CNN网络效果 Faster R-CNN Fas ...

  3. KMP算法详解及各种应用

    KMP算法详解: KMP算法之所以叫做KMP算法是因为这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字.其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题 ...

  4. 字符串匹配之KMP算法详解

    kmp算法又称"看毛片"算法,是一个效率非常高的字符串匹配算法.不过由于其难以理解,所以在很长的一段时间内一直没有搞懂.虽然网上有很多资料,但是鲜见好的博客能简单明了地将其讲清楚. ...

  5. KMP算法详解P3375 【模板】KMP字符串匹配题解

    KMP算法详解: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt(雾)提出的. 对于字符串匹配问题(such as 问你在abababb中有多少个 ...

  6. KMP算法详解及代码

    KMP算法详解及代码 KMP算法详解及代码 定义及应用 理论 基本概念 next 数组 总结 注意 代码 KMP算法详解及代码 最近正好在看字符串相关的算法内容,就顺便把KMP算法回顾了一下.相应的代 ...

  7. 奇淫巧技的KMP算法--详解

    奇淫巧技的KMP算法–详解 花了一下午时间,看了十几个博客,终于拿下了KMP高地,现在总结下下自己对KMP的理解和实现. 情景1 假如你是一名生物学家,现在,你的面前有两段 DNA 序列 S 和 T, ...

  8. 人脸识别系列三 | MTCNN算法详解上篇

    前言 我们前面分享了PCA,Fisher Face,LBPH三种传统的人脸识别算法,Dlib人脸检测算法.今天我们开始分享一下MTCNN算法,这个算法可以将人脸检测和特征点检测结合起来,并且MTCNN ...

  9. KMP 算法详解(CPP 实现)

    转载请标明出处:https://blog.csdn.net/kiss0tql/article/details/81416283 本文来自:deemo的博客 说明 kmp 算法思想 next 数组计算 ...

最新文章

  1. Nacos配置中心原理
  2. LeetCode Third Maximum Number
  3. idea设置包为层级结构?
  4. Spring Security 应用详解 集成SpringBoot —— 简单入门
  5. java web开发技巧_java web开发技巧
  6. 推荐几本书,这些书.Net程序员最好要看
  7. BCrypt加密怎么存入数据库_dns污染怎么解决
  8. java性能优化文章
  9. 算法 判断多个点是否在同一圆周线上_广州灵活计费自动出盘机技术方案大盘点...
  10. 2018年最新桌面CPU性能排行天梯图(含至强处理器)
  11. CCS软件的C语言取模注意点
  12. 从概念到应用,腾讯视角深入“解剖”AI平台和语音技术
  13. 一个app项目如何从想法一步一步落地?有哪些必要的流程?
  14. 【EasyUI篇】Combo自定义下拉框组件
  15. 强化学习与Deep Q-Network(DQN)
  16. 机器人摘果子看图写话_二年级摘苹果看图写话范例
  17. 【微前端】什么是微前端
  18. 错误:Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/db.properties]
  19. 微软surface屏幕抖动_Microsoft放弃Windows E并显示浏览器投票屏幕
  20. 关于阿里矢量图iconfont的应用

热门文章

  1. 2018年6月计算机一级试题答案,2018年计算机一级考试试题及答案.doc
  2. wdcdn多节点分布式CDN系统1.3发布
  3. CU访谈录:DTCC讲师徐冬奇分享技术路上的挑战、踩坑、痛苦、成长 【总结分析】
  4. 鸿蒙系统基于安卓是什么意思,华为鸿蒙系统基于安卓还是Linux呢?
  5. Matlab中两种矩阵除法运算:右除 / 和左除 \ 的区别
  6. Web前端学习笔记09:移动web开发流式布局_flex布局
  7. 多测师肖sir_高级金牌讲师_杭州面试之纬创2021年-4月 -8日
  8. char类型函数(C++入门?)
  9. 步进电机的“嗡嗡嗡”噪声来源及其解决方法
  10. 打开APP手机提示APP被列为风险软件