转载请标明出处:https://blog.csdn.net/kiss0tql/article/details/81416283
本文来自:deemo的博客

  • 说明
  • kmp 算法思想
  • next 数组计算
  • 字符串匹配
  • 参考资料

说明

  kmp 算法是由 Knuth、Morris 和 Pratt 三人设计的一个非常高效的字符串匹配算法。该算法用于判断一个字符串是否包含于另一个字符串中。该算法复杂度是 O(m+n)O(m+n)O(m + n),其中 mmm 和 n" role="presentation">nnn 分别是两个字符串的长度。

kmp 算法思想

  我们能很容易想到 O(M∗N)O(M∗N)O(M * N) 复杂度的算法,即索引从字符串 str1 的第一个字符开始,依次检查以该字符作为首字符的连续 N 个字符是否与字符串 str2 匹配,若不匹配,则索引向前移动一位,从第二个字符开始依次检查,以此类推。

  该算法并没有考虑到现已匹配的部分字符串的结构。已匹配的部分字符串中可能包含相同的部分,若利用这些相同的部分,我们可以一次性移动多位而不只是移动一位,这样大大提高了算法的效率。

  假设现有两个字符串 str1 和 str2,并且现已从 index1 处开始检是否匹配。当我们检测到 index2 处时,发现此时待检测的两个字符不相同(即图中 A 和 B 部分字符不匹配,index2 和 index1 之间的字符均匹配),按照传统思想我们将会从 index1 + 1 处的位置开始新一轮的检测,这样检测的效率极低。当我们仔细观察现已匹配的部分字符串时,若能找到字符串中首尾相同的部分(即图中 1、2、3和4部分的字符串相同),则可以直接将 str2 向前移动 k 个位置(如图所示),即图中 2 和 4 部分将作为新匹配字符串的头部,并接着从 str1 的 index2 位置开始检测。若不含有相同的部分,此时不管是将 str2 向前移动多少位都不会出现匹配的字符串,因此,可以从 str1 的 index2 位置开始新的一轮检测(即从 str2 的第一个字符开始匹配,可以理解为将 str2 向前移动了 str2 长度的位置)。

  现有如下两个字符串:

    const char *str1 = "bacbbacabadababacambabacadbacabacasdsd";const char *str2 = "bacabaca";

  按照上述的匹配过程可以得到如下的匹配过程图,当匹配到 index2 处时,发现字符 d 和 字符 c 不匹配,但已匹配的部分 bacaba 的首位相同的部分为 ba,该部分为字符串 bacaba 的最长的相同前缀和后缀,而在下一轮的匹配过程中,将 ba 部分作为新的首部,并接着 index2 处开始匹配。一次类推完成整个字符串的匹配过程。

  由以上匹配过程可知,每当开始新一轮的匹配时,我们需要知道当前已匹配的字符串中最长相同前缀和后缀的长度,以实现匹配过程中的快速跳转。而当前已匹配的字符串长度可能为 1 ~ length(str2),因此,我们可以创建一个长度为 n 的数组 ( n = length(str2) ),用来保存不同长度字符串的最长相同前缀和后缀的长度。该数组为 next 数组。

next 数组计算

  next 数组中第 i 个数表示字符串索引从 0 到 i 的字符串最长相同前缀和后缀的长度。注意,前缀和后缀不包括字符串本身,比如 aaaa 相同的最长前缀和最长后缀是 aaa
  假设我们现在已经求得 next[0]、next[1]、…… next[ i-1 ],分别表示长度为 1 到 i 的字符串的最长相同前缀和后缀的长度。现在要求 next[ i ]。由下图我们可以看到,如果位置 i 和位置 next[ i-1 ] 处的两个字符相同(下标从零开始),则 next[ i ]等于 next[ i-1 ] + 1。

  如果两个位置的字符不相同,我们可以将长度为 next[ i-1 ] 的字符串继续分割,获得其最大公共长度 next[ next[ i-1 ] -1 ],然后再和位置 i 的字符比较。由于长度为 next[ i-1 ] 的字符串的公共长度保存在索引为 next[ i-1 ] -1 的 next 数组中,而该字符串又包含相同的前缀和后缀,如下图所示,如果位置 next[ next[ i-1 ] -1 ]和位置 i 的字符相同,则 next [i ]就等于 next[ next[ i -1 ] -1 ] + 1。如果不相等,就可以继续分割长度为 next[ next[ i -1 ] -1 ] 的字符串,直到字符串长度为 0 为止。

  根据上述计算 next 数组的过程,可以写出相应的求 next 数组的代码(CPP实现):

vector<int> getNext(const char *str)
{int len = strlen(str);               // 字符串长度vector<int> next(len, 0);            // 保存结果,next[0]=0for(int i = 1; i < len; i++){int k = next[i - 1];             // k 表示需要比较的位置,初始值为 next[i - 1]while(k > 0 && str[i] != str[k]) // 比较,若不相等则继续分割,直到相等或为0(即不含相同部分)k = next[k - 1];if(str[i] == str[k])             // 若相等,则 next[i] = k + 1,否则为0,其中 k 为索引k++;next[i] = k;                     // 更新 next[i]}return next;
}

  在求解 next 数组后,可以利用该数组进行字符串匹配。

字符串匹配

  假若当前字符串 str1 的第 i 个字符正在与字符串 str2 的第 k 个字符进行匹配(i 和 k 均从 0 开始),若相等,则进行下一轮的匹配,即进行 str1 的 第 i+1 个字符与 str2 的第 k+1 个字符匹配;若不相等,则跟 str2 的第 next[k - 1] 字符进行匹配,直到 k = 0 为止。重复以上过程直到检测完 str1 的所有字符。

  根据上述匹配过程,完成 CPP 代码:

int search(const char *str1, const char *str2)
{vector<int> next = getNext(str2);           // 获得 str2 的 next 数组int k = 0;                                  // 记录当前已匹配 str2 的索引int res = -1;                               // 保存匹配的字符串起始位置,若不存在,返回-1for(int i = 0; i < (int)strlen(str1); i++)  // 第 i 轮匹配{while(k > 0 && str1[i] != str2[k])      // str1的第i个与str2的第k个字符进行比较,若不同,则k=next[k-1],直到k为0或相等为止k = next[k - 1];if(str1[i] == str2[k])                  // 若相等,更新kk++;if(k == (int)strlen(str2))              // 若找到完全匹配{res = i - k + 1;                    // 保存匹配的字符串起始位置,此时根据需要可用容器保存多个结果k = next[k - 1];                    // 进行下一轮匹配,此处根据需要可去掉}}return res;
}

  以上就是 KMP 算法的整个过程,完整的代码可访问 Github 下载。

参考资料

  • KMP算法学习(详解)
  • KMP算法最浅显理解 —— 一看就明白

以上内容已同步至 Github。

KMP 算法详解(CPP 实现)相关推荐

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

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

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

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

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

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

  4. KMP算法详解及代码

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

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

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

  6. 【KMP算法详解——适合初学KMP算法的朋友】

    相信很多人(包括自己)初识KMP算法的时候始终是丈二和尚摸不着头脑,要么完全不知所云,要么看不懂书上的解释,要么自己觉得好像心里了解KMP算法的意思,却说不出个究竟,所谓知其然不知其所以然是也. 经过 ...

  7. 【转】KMP算法详解

     原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任. http://billhoo.blog.51cto.com/2337751/411486 ...

  8. [数据结构]模式匹配算法--KMP算法详解

    目录 一. 模式匹配 二. 模式匹配算法 1. 朴素模式匹配算法 2. KMP算法 1). KMP算法的优势 2). KMP算法的原理 3). next数组的构造 4). 利用next数组匹配的过程 ...

  9. KMP算法详解 转帖

    个人觉得这篇文章是网上的介绍有关KMP算法更让人容易理解的文章了,确实说得很"详细",耐心地把它看完肯定会有所收获的--,另外有关模式函数值next[i]确实有很多版本啊,在另外一 ...

最新文章

  1. 【ZooKeeper Notes 28】ZooKeeper典型应用场景一览
  2. 介绍几个好用的android自定义控件
  3. windows 任务管理器,查看进程id,进程标识符pid
  4. Shell(8)——for、while、until
  5. LiveVideoStackCon 北京站,好久不见
  6. Nginx Rtmp Module - HLS切片和级联播放
  7. springboot 各种日志打印
  8. 语音识别(四)——DTW, Spectrogram, Cepstrum Analysis
  9. java选择题多选题系统小程序_单选题与多选题判断得分(自动考试小程序,入门级)...
  10. 领域应用 | 小米在知识表示学习的探索与实践
  11. 安全狗远程3389端口修改工具
  12. 软件设计师考试 | 第四章 操作系统知识 | 文件管理
  13. 性能测试 - 响应 vs 延迟 vs 吞吐量 vs 负载 vs 扩展性 vs 压力 vs 健壮性
  14. 机器学习基础概念(三):归纳与演绎
  15. C++ 两点之间的距离
  16. 计算机论文源码重复太多,知网查重代码重复率太高怎么办
  17. “振心计划”受益房东超20万,爱彼迎中国活跃房源同比增长超两成
  18. 操作系统第2章作业题答案
  19. Smartbi报表工具的学习笔记,如何学好报表分析?
  20. 达观数据:Selenium使用技巧与机器人流程自动化实战

热门文章

  1. 霍尔元件测速应用的困惑
  2. じゅうきゅう: ANN
  3. 在PPT中插入组织结构图ppt模板大全
  4. SiT9366:0.23ps超低抖动1-220MHz差分晶振,LVPECL/LVDS/HCSL
  5. 多版本(30)并行控制的解决方案
  6. 最全数学建模软件资源分享(origin中文版、matlab、visio、spss)亲测全部可用!!!安装问题可用评论交流
  7. 接口(1):抽象类和抽象方法、接口
  8. Android kotlin 将Base64字符串转换成Bitmap,并在jetpack compose的Image控件中显示
  9. 【开发经验】redis实现共同好友功能
  10. SpringBoot ( 三 ) 转页