文章目录

  • 一:KMP算法解决的问题
  • 二:详解KMP
    • (1)暴力匹配的缺点
    • (2)最长相同前缀和后缀
    • (3)究竟怎么回溯
    • (3)next数组
    • (4)求解next数组
      • A:next[0]=-1
      • B:next[j]=k
      • C:k=next[k]
    • (5)KMP算法代码

一:KMP算法解决的问题

如下有两个字符串,问你:如何快速在主串中找到目标串

相信大家想到的第一个解决方法就是暴力破解——用两个指针,开始时分别指向两个串的开头,然后比较,如果相同继续扫描下一个字符,一旦出现不相同,两个指针进行回溯,主串的指针回溯到下一位,目标串的指针回溯至第一位,然后继续按照上述步骤比较,如下图

-注意:回溯的步骤在图中表示为了目标串向后移动的,但是实际物理内存中字符串是不可能移动的,这里只是为了形象解释

由此,你可以轻松写出对应代码

int index(string main_str,string aim_str)
{int i=0;int j=0;//i和j分别用来扫描主串和目标串while(i<=main_str.size()-1 && j<=aim_str.size()-1){if(main_str[i]==main_str[j]){++i;++j;}else{j=1;i=++k;}}if(j>aim_str.size()-1)return k;elsereturn 0;
}

暴力匹配是可行的,但是时间复杂度一看就很高,所以KMP算法的出现正是为了解决 “如何在主串中快速找到目标串”

二:详解KMP

为了便于介绍,我将主串和目标串设置为这样

  • 当然,下面的例子中目标串并未出现在主串中,但不要紧,这里只是为了说明更好的说明

(1)暴力匹配的缺点

通过上面的叙述,大家可能已经发现了暴力匹配的缺陷所在——回溯太过频繁了。只要出现不匹配就需要回溯到下一位

这样无脑回溯其实并不合理,比如下面这种情况中,如果让你进行回溯的话,你肯定不会直接回溯到下一位,起码也得是下面这样吧

  • 这是因为你明白直接回溯到下一位会导致连第一位都不会匹配,就更别谈后面了

所以KMP的算法的核心点就在这里:不要回溯到无效的地方,让其回溯到有效的位置

(2)最长相同前缀和后缀

在继续讲述之前,必须引入一个概念——一个字符串的最长相同前缀和后缀。要想知道什么是最长相同前缀和后缀,首先得明白什么是字符串的前缀和后缀,相信看完下面这个图你就不难理解了

那么,最长相同前缀和后缀你也应该能找出来了吧

  • 什么?竟然不是最下面的那个吗?这里就要给大家说明一点,最长相同前后缀不能是字符串本身,因为如果你认为最长相同前后缀可以是字符串本身的话,那么这就是一个恒成立的命题了,那么再继续讨论就没有意义了

(3)究竟怎么回溯

继续使用上面那个“你认为应该这样回溯”的例子

仔细观察你会发现它回溯的位置有些特点?没错,就是最长公共前后缀重合的地方,也就是说让最长相同前缀对齐至后缀

不仅仅是这样,继续观察,目标串发生不匹配时其索引为5,索引5之前的字符串为“ABCAB”,其最长相同前后缀长度为2,同时目标串中也正好是索引为2的地方(也即字母C)与主串发生不匹配的地方(也即字母E)对齐

这意味着所谓让最长相同前缀对齐至后缀其本质就是:记目标串发生不匹配的地方前的字符串的最长相同前后缀长度为 l l l,然后回溯时让目标串索引为 l l l的地方对齐至此次不匹配的地方

这里我们可以再举几个例子

①:如下目标串的索引为3号的位置发生不匹配

其3号位置前的字符串的最长相同前后缀的长度是1,所以就应该让目标串索引为1的位置与该不匹配处“对齐”

②:如下目标串的索引为5号的位置发生不匹配

其5号位置前的字符串的最长相同前后缀的长度是2,所以就应该让目标串索引为2的位置与该不匹配处“对齐”

讲到这里,大家可能也应该意识到了,这意味着匹配时根本就“不需要”主串,因为每个位置发生不匹配时,总有一个值能确定其应该回溯的位置。这个值就是其前面的字符串的最长相同前后缀的长度

(3)next数组

我们把目标串每个位置发生不匹配时,目标串应该回溯的位置给记录下来,形成一个数组,这个数组就是所谓的next数组。next[i]=j,就表示索引为i的位置发生不匹配,就让其回溯到j的位置继续匹配

next数组的计算方法我其实前面已经说到过了,如下目标的串next数组计算如下,需要注意是由于第一个位置也就是next[0]前面没有串,所以记为-1

A B C A B C M N
next[0] next[1] next[2] next[3] next[4] next[5] next[6] next[7]
-1 0 0 0 1 2 3 0

因此依据next数组我们就能确定目标串的回溯位置了,比如下面假设目标串和主串在索引为4的位置发生不匹配,所做的操作如下

(4)求解next数组

KMP算法的思想大部分人是能很轻松的明白的,但是这个算法为什么劝退了很多人呢?就是无法准确,深刻的理解求解next数组的代码,如下

  • 上述代码中总共有三个地方如果能理解清楚这个代码也就没问题了。
    next[0]=-1,next[j]=l,k=next[k],其中k=next[k]最让人摸不着头脑
void getnext(string target_str,int next[])
{int j=0,k=-1;next[0]=-1;while(j<target_str.size()){if(k==-1 || str[j]==str[k]){j++;k++;next[j]=k;}else{k=next[k];}}
}

A:next[0]=-1

如下,next[0]=-1是指当目标串的第一个位置发生不匹配时,应该让目标串的索引为-1的位置与当前不匹配的地方对齐

  • 你肯定会问,怎么可能有-1的位置啊,这明显越界了啊?

这里我们先截取KMP算法的一小段(不要担心你肯定看得懂)

  • 下面的j=next[j]指的就是发生不匹配时应该如何回溯

因此当代码运行到这里时就会出现j=next[0]=-1,然后重新回到循环,继续判断,此时j=-1,进入if内,j++很明显会等于0,i++会到主串的下一位。所以它的意思就是让目标串的第一位与主串的下一位进行比较

  • 现在你应该能理解为什么要设置next[0]=-1了吧

B:next[j]=k

  • 对于next[j]=k我认为大家肯定能明白的一点就是在为next数组进行赋值,但是不明白的是为什么下标为j的元素的值是k

首先请大家考虑哪种情况下会有next[j]=k?根据上面的代码自然是k=-1str[j]=str[k]的时候

k=-1时,总会有k++,使得k变为0,这样的话next[j]=0,表明前面没有相同的前后缀

一旦k不是-1,满足这个if的唯一条件就是str[j]=str[k]了。如果此时str[j]=str[k]这就表示此时的位置可以算作相同的前后缀了,我们所做的就是判断下一位了

比如下面,可以看出,此时k来到了C的位置,j也来到了C的位置,他们之前都各自在B的位置,由于相同,所以来到了现在位置,现在他们要判断此刻对应的位置是否可以也将其算入相同的前后缀

非常幸运,他们相同,于是j++,k++,next[j]=3

C:k=next[k]

  • KMP算法最难理解的地方就在这里,为了方便说明,我换了一张图

如下,jk准备比较下一个

很遗憾这个位置,无法使其成为长度为4的相同前后缀,那么根据代码只能执行k=next[k]了。这样画比较难受,我把前缀直接拿到下面来

有没有似曾相识的感觉?没错,好像是主串和目标串,但这不是原来的那个两个串。而是后缀作为了主串,前缀作为了目标串,好的,现在前缀(目标串)的索引为3的位置发生了不匹配,该怎么做?自然是让前缀(目标串)下标为1的位置(next[3]=1)与该不匹配处重新比较

好的,重新组装回去,毕竟这不是真的主串和目标串

现在if继续判断,当然满足,对应位置相同,那么继续i++j++,于是next[9]=2,此时next数组全部赋值完成。

仔细想一想,下面的这种情况中已经不可能有ABAB,ABAC相等的情况存在了,因为一旦相同,那么next[9]=4了,也即是C之前不可能有长度为4的相同的前后缀,但是没有这么长的或许也有短的呀,你看咋们上面的那个AB=AB不就是一个短的情况吗,所以这也就是为什么有k=next[k]这样的回溯的情况。其实就是在前缀和后缀在比较,只不过一旦遇到不相等,前人栽树后人乘凉罢了!——其实这本质是动态规划

(5)KMP算法代码

我觉得不用我再解释了吧

int KMP(string main_str,string target_str)
{int next[MaxSize],i=0,j=0;getnext(target_str,next);while(i<main_str.size() && j<target_str.size()){if(j==-1 || main_str[i]==target_str[j]){i++;j++;}else{j=next[j];}}if(j>=target_str.size())return i-target_str.size();//返回目标串在主串的第一个字符的位置elsereturn -1;//找不到返回-1}

KMP算法图文详解(为什么是next[0]=-1、next[j]=k和k=next[k])相关推荐

  1. [转]数据结构KMP算法配图详解(超详细)

    KMP算法配图详解 前言 KMP算法是我们数据结构串中最难也是最重要的算法.难是因为KMP算法的代码很优美简洁干练,但里面包含着非常深的思维.真正理解代码的人可以说对KMP算法的了解已经相当深入了.而 ...

  2. Dijkstra算法图文详解和C++代码

    文章目录 1 Dijkstra算法基本原理 2 算法过程图解1(有向图) 3 算法过程图解2(无向图) 4 C++代码 4.1 案例1代码 4.2 案例2邻接矩阵定义 4.3 案例2代码Dijkstr ...

  3. 数据结构KMP算法配图详解(超详细)

    KMP算法配图详解 前言 KMP算法是我们数据结构串中最难也是最重要的算法.难是因为KMP算法的代码很优美简洁干练,但里面包含着非常深的思维.真正理解代码的人可以说对KMP算法的了解已经相当深入了.而 ...

  4. 字符串算法——KMP算法C++详解

    简介 KMP 算法是 D.E.Knuth.J,H,Morris 和 V.R.Pratt 三位神人共同提出的,称之为 Knuth-Morria-Pratt 算法,简称 KMP 算法.该算法相对于 Bru ...

  5. KMP算法原理详解_论文解读版

    1. KMP算法 KMP算法是一种保证线性时间的字符串查找算法,由Knuth.Morris和Pratt三位大神发明,而算法取自这三人名字的首字母,因而得名KMP算法. 那发明这样的字符串查找算法又有什 ...

  6. 什么是KMP算法(详解)

    什么是KMP算法: KMP是三位大牛:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的.其中第一位就是<计算机程序设计艺术>的作者!! KMP算法要解决的问题就是在字 ...

  7. 学习KMP算法(详解)

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

  8. 七大排序算法—图文详解(插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序)

    作者:渴望力量的土狗 博客主页:渴望力量的土狗的博客主页 专栏:数据结构与算法 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器--牛客网 点击免费注册和我一起刷题吧 目录 插入排序: ...

  9. Dijkstra算法图文详解

    Dijkstra算法算是贪心思想实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更 ...

最新文章

  1. Share Point 2013使用Windows PowerShell 获取,删除UserProFile
  2. 酷冷至尊官方psu计算工具_静静的挺你10年:酷冷至尊V650 GOLD全模组电源体验
  3. redis php 书,php中redis的使用
  4. 易语言写c盘配置文件,易语言写配置文件的方法
  5. CSS 背景尺寸 background-size属性
  6. 【华为云技术分享】STM32L476移植华为LiteOS系列教程---开发前的准备 2
  7. 张季跃 201771010139《面向对象程序设计(java)》第八周学习总结
  8. 有机食品农产品电商网站HTML模板
  9. 32 道常见的 Kafka 面试题
  10. 慕测安居客功能测试答案
  11. 【Linux系列文章】正则表达式与文本处理工具
  12. python熊猫图案_熊猫备忘单–适用于数据科学的Python
  13. Oracle 查询的十个小技巧
  14. spring boot日志配置文件(彩色日志)logback-spring.xml
  15. 弹性云服务器---ECS
  16. STM32CubeIDE导入机智云生成基于MDK的STM32工程
  17. Python是一种开源的、免费的、通用的脚本语言
  18. Python制作小软件——1. 安装并使用PyQt5进行界面设计
  19. python数据包分析_用python编写脚本分析网络数据包
  20. appwidget百度桌面搜索

热门文章

  1. 谈谈QUIC协议原理
  2. MySQL、SqlServer、Oracle 三种数据库的优缺点总结
  3. 数据库概论学习笔记——关系数据理论
  4. OK资本周子涵:区块链的成果是“熬”出来的
  5. Android native层Hander原理分析
  6. 快牛策略——嵌入式计算机
  7. 联想服务器gpt如何修复,电脑GUID格式GPT硬盘的引导如何修复|GUID的GPT硬盘引导损坏了怎么办-系统城...
  8. Linux安装cuda10.2
  9. MQ高级(四)MQ集群
  10. 实现AlphaBlend效果时的图标预处理