最容易理解的KMP算法推演(包括next数组的理论推导)

BF算法(暴力求解)

我们直接看一个例子:

文本串: a b c b e f g

模式串: b e f

题目:在文本串中找到模式串并返回其所在位置.

整体来说就是不断从文本串的不同位置开始与模式串匹配,直到匹配上.

KMD算法

好,我们改变一下题目:

文本串:a b a a b a a d e       

模式串:a b a a d e

题目:在文本串中找到模式串并返回其所在位置.

BF算法当然可以解决,但是分析一下,需要判断4次才能得到正确起始位置.

这样,我们引入了KMP算法,它可以高效的解决这类文本串含多个模式串起始字符的查找问题.

下面给出思路:

首先,我们先把两个字符串匹配一次:

如图,匹配到 i 位置停止.

下一步, i 不动,从模式串0~j -1 号位之间分别找以0号位开头的字符串(记为s1)和以 j-1 号位结尾的字符串(记为s2),这两个字符串必须相等(尽可能长),且长度不能为 j .

如图:s1的长度正好就是 j 要移动到的位数,也就是是1号位.

j 和 i 继续匹配,而此时我们检查的正是从文本串3号位开始的字符串是否与模式串匹配,其中3号位肯定匹配.

为什么这样做?

我们拉回之前的图:

把下一次文本串的起始位置记为start ,start = 2/3.

首先,既然已经匹配失败停止到 i 位上,那么模式串肯定比 0 ~i -1(0~j-1)长,更比start~i-1(j-1)长,所以如果起始位置为start的文本串能和模式串匹配成功的话arr1[start]~arr1[i-1]肯定能和模式串匹配(总体相等,部分也相等),如果连arr1[start]~arr1[i-1]都匹配不上也就没必要继续了(自己举个反例子体会一下).而arr1[start]~arr1[i-1]匹配的一部分模式串为arr2[0]~arr2[i-start],即arr1[start]~arr1[i-1]==arr2[0]~arr2[i-start](1)

又因为arr1[0]~arr1[i-1]==arr2[0]~arr[ j-1],所以必有arr1[start]~arr1[i-1]==arr2[start]~arr2[ j-1](2)(总体相等,部分也相等)

联立(1)(2)得arr2[0]~arr2[i-start]==arr2[start]~arr2[ j-1]

显然,3满足,2不满足,所以start=3,

这解释了

从模式串0~j -1 号位之间分别找以0号位开头的字符串(记为s1)和以 j-1 号位结尾的字符串(记为s2),这两个字符串必须相等(尽可能长)

至于尽可能长,则是因为越长,文本串起始位置越往前,防止漏掉,当然匹配不上就短一点,就像上文start=3却!=2.

长度不能为 j

如果长度为  j 的话,那不就是刚匹配过的字符串,很显然匹配失败.

直到现在我们终于可以把 j 移动了

如图:s1的长度正好就是 j 要移动到的位数,也就是是1号位.

然后比较 i 和 j 是否匹配

这解释了为什么 i 不用移动!!!(看(2)式,前面都相等了,i 还退回去比个集贸)

最后一个疑问,确定 j回退的位置貌似更加繁琐.可惜,前人为我们创造了next数组,可以显示出每个位置的回退位置,它被公式推导出来.

给next数组赋值的推导过程:

首先,无论何时

next[0]=-1(在下文)

next[1]=0;

下一步

设next[i]=k(k为 j 回退到的位置);

arr2[0]~arr2[k-1]==arr2[start]~arr2[ j-1]

观察发现start = j - k;

故 arr2[0]~arr2[k-1]==arr2[ j- k]~arr2[ j-1](3)

if arr2[k]=arr2[ j ]

则 arr2[0]~arr2[k-1]+ar2[k]==arr2[ j- k]~arr2[ j-1]+arr2[ j ]

即 arr2[0]~arr2[k]==arr2[ j- k]~arr2[ j](4)

(3)可得next[i].所以在arr2[k]=arr2[ j ]条件下,(4)得next[ j+1]=k+1 ((3)和(4)对比一下即可)

if arr2[k]!=arr2[ j ]

让k充当j继续回退!直到找到一个arr2[k]=arr2[ j ]!然后next[i+1]=k+1

只要分析一下无论是哪个回退点,都有arr2[0]~arr2[k-1]==arr2[start]~arr2[ j-1], arr2[0]~arr2[k-1]是哪个回退点产生并不重要,重要的是只要找到arr2[k]=arr2[ j ],我们就可以next[i+1]=k+1.

如果回退到0位还没有arr2[k]=arr2[ j ]怎么办?

next[0]=-1;

然后直接next[ j+1]=k+1=0;(也许是归纳法,哪位大佬解释一下-_-)

准备完毕,上代码:

OVER!

  • 36
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值