KMP算法总结(一)

这篇博客主要分享一下KMP算法的操作:

本篇博客思想非本人原创,主要来源于B站大佬的一个教程视频:KMP算法  ,但是码字和图片都是本人亲手做的哈,

首先我们有两段字符串:

我们要做的就是来看一下在T里面能否找到P这段字符串,用肉眼我们应该能看到,当匹配到T里面下标为5的时候,是匹配上了的。我们一般用的解法就是暴力匹配法:首先看T[0]和P[O]是否匹配,匹配上了,再看T[1]和P[1]是否匹配,匹配上了,再看T[2]和P[2]是否匹配,匹配上了,T[3]和P[3]是否匹配,匹配不上。然后将P整体向后移动一格,再继续进行比较。依此类推,这种算法叫暴力搜索法,就是没有任何的算法,直接进行比较,这种算法简单易懂,但是这种算法的时间复杂度太高了,最坏的情况下,这种算法的时间复杂度可以达到O(m*n),所以这种办法是并不可行的。

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。

我们可以看到,KMP算法直接将算法的时间复杂度直接降到了线性的数量级,所以是非常有效的字符串匹配算法。下面我们来看一下KMP算法的基本操作:

  1. 做KMP算法之前,我们首先需要对待匹配的字符串(也就是上面的P这个字符串)算出一个叫前缀表(prefix-table)的数组,也就是我们说的next数组。

(1)数组里面存放的数字是字符串的最长公共前后缀的数量,要写前缀表,我们先要知道什么是前缀:前缀就是这个字符串前面的字母组成的字串。例如P的前缀如下:

(2)接着我们要找出其每一个前缀的公共前后缀的长度,将其结果保存在数组里:"前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

“a” 的前缀和后缀都是空集,公共前后缀的长度为0。

“a  b”的前缀为[a],后缀为[b] ,公共前后缀长度为0。

“a  b  a”的前缀为[a]、[a  b] ,后缀为[b  a]、[a],公共前后缀为[a],即长度为1。

“a  b  a  b”的前缀为[a]、[a  b]、[a  b  a] ,后缀为[b  a  b]、[a  b]、[b],公共前后缀为[a  b], 长度为2。

“a  b  a  b  c”的前缀为[a]、[a  b] 、[a  b  a] 、[a  b  a  c]后缀为[b  a  b  c] 、[a  b  c] 、[b  c] 、[c],可以看出最长公共前后缀的长度为0。

做完这些,我们的prefix-table即将做好了

为什么是即将做好呢?因为我们还差一步:将数组整体后移一位,空出来的第一位部-1,具体为什么这么做,咱也不知道,咱也不敢问!完成后的prefix-table就长下面这个亚子:(实际数组里面只有数字,只是为了匹配时方便观察将字母添加进去

(3)然后我们就可以利用这个前缀表来进行KMP字符串匹配了:

  • 首先将两个字符串放在一起,也是从第一个字母进行匹配:

  • 我们可以看到,当匹配到下标3的时候,匹配失败了,这时候我们看一下匹配失败位置的prefix-table相同位置存放的数字是多少,然后将字符串移动到对应的位置就好了: 在“b”的位置匹配失败,对应位置数字为1,我们就将待匹配的字符串下标为1的位置移动到刚刚匹配失败的地方,从下标为1 的位置开始进行匹配:

  • “a”和“b”直接匹配失败,我们看对应位置的prefix-table存放的数字为0 ,还是继续上面同样的操作,prefix-table存放的数字为0,我们就将待匹配的字符串下标0位置和刚刚匹配失败的地方对齐:

  • “a”和“a”匹配成功,“c”和“b”匹配失败,然后进行同样的操作:

  • “c”和“a”匹配失败,同样进行操作,此时的prefix-table位置为-1,也就是待匹配字符串下标为-1的位置移动到匹配失败的位置,这也就等同于将整个待匹配的字符串整体向后移动一位:

  • 移动完以后,继续进行匹配操作,我们发现,现在字符串完全进行匹配。很棒!但是这时候如果要求我们将所有的符合要求的字符串都返回回来我们应该怎么做呢?操作还是和上面一样的:这时待匹配字符串最后一位对应prefix-table数字为2,我们将待匹配字符串的下标为2的位置对齐刚刚匹配的最后一位:

  • 继续按照上面的操作,进行匹配,直到主串剩余的字符串的长度小于待匹配的字符串的长度,则匹配结束。

好啦!今天先将具体的操作讲完,下一篇博客再说一下,代码该如何写。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值