通俗易懂KMP模式匹配法及代码实现

最近碰到一道KMP字符串匹配题目,首先觉得用自己的笨办法应该也能做,子字符串分别于母字符串匹配,若匹配不成功右移一位,这样做对于简单字符串可以在规定的时间内完成,但是对于稍复杂字符串则匹配时间过长,超出规定的运行时间。于是学习了KMP模式匹配法。

什么是KMP模式匹配

KMP模式匹配是一种高效的字符串匹配方法。它最大的特点就是充分利用的模式串(也就是子串)的隐含内在特点,匹配过程不再像传统方法那样”匹配失败右移一位“,而是“科学性”的移位匹配,从而大大减少了不必要的匹配工作量。KMP匹配与传统字符串匹配法的差别可由下图形象的看出:
字符串匹配
当我们匹配上图两个字符串时,在子串第四个字符出现匹配不成功,传功方法的下一步匹配是下面这样的:

传统方法匹配
而KMP模式匹配法的下一步是这样进行的:
KMP模式匹配
由上图可以看出,KMP模式匹配法直接跳过了B跟C来到了与其首字符匹配的位置,高效而准确。

next[]数组

前面介绍了KMP模式匹配法其高效的原因就是充分利用了模式串本身存在的特性。这个特性归纳成为KMP算法的next数组,也是KMP算法的核心。
next数组长度与模式串长度相同,每个数组元素代表在模式串中在该位置以前的字符串的前后缀字符相同的长度。举例来说,上面的ABCDADE模式串中,next[4]=0,next[5]=1。因为next[4]是计算ABCD字符串中前后缀相同长度,其长度为0;而next[5]则是计算ABCDA的前后缀长度,可以看出是1.
当然啦,不能全部用人工来观察其前后缀长度,而是要用科学方法。这也就是KMP算法的核心——next数组的求取。

next[]数组是如何来的

next数组的求取方法简单说分为两步:
1)递归求取各子串前后缀相同的长度组成的一个数组。
2)将该数组第一个元素置为-1,其他元素设为前一个元素值即,p[j]=p[j-1]
由此,KMP核心转化为求取模式串的各个子串的前后缀相同的长度。

子串前后缀相同的长度值计算

对于模式串P的前j+1个字符,设前j个字符的前后缀相同的长度为k,最后求得的前后缀相同的长度数组记为S[],则:
1)求取递归的前两项:
S[0]肯定为0,因为它长度一共为1,不存在前后缀字符;
S[1] = P[0] == P[1] ? 1: 0;
2)若P[j] == P[k],则S[j] = next[j+1] = k+1;
3)若P[j] != P[k],此时令k=next[k]=S[k-1],若P[j] ==P[k],则S[j] = k+1;若一直不相等,直到k=0还不相等,则S[j] = 0;

个人对于前后缀长度算法的理解

光看上面的一对公式,大部分人会一头雾水,为啥这么算,还非要死记硬背下来才行吗?下面是我对上面计算方法的理解,希望可以帮助初学者理解掌握。
首先上述计算方法的核心是k值,要深刻理解这个k值。k值就是模式串的前j个字符所具有的最大前后缀相同的长度值。也就是代表该子字符串的前k个字符与后k个字符相同。
那么,在求取S[j]时2时,S[j]就会优先与其签名字符串的第k+1个字符相比较,映射到数组中就是字符P[k],若结果相同,则前后缀相同长度自然而然就会增加1;若P[k]与P[j]不相同,此时就应该回退查找更短的字符串,则先求取k前面的字符串的最大相同前后缀长度,然后判断在该字符串相同前后缀的下一个字符,是否与P[j]相同,若不相同则一直如此循环,直到k=0,也就是第一个字符是否与P[j]当等,若相等,S[j]=S[k]=S[0]+1=1,若不相等,则为0。

参考示例

模式串P为 ABAD
1)S[0] = 0;S[1] = 0;
2)求S[2]:k = 0,P[2] = = P[0],所以S[2] = S[1] + 1 = k + 1 = 1(S数组元素的值就是k值)
3)求S[3]:k=1,P[3] != P [1],继续 k = S[k-1] = S [0] = 0,P[k] = A,也与P[3]不同,k已经为0,结束递归,S[3] = 0。
综上,得到前后缀相同长度数组S为:
S 0 0 1 0
则,next数组根据前面方法可得出:
next -1 0 0 1

代码实现

求模式串的next数组

求next数组

根据next数组匹配字符串

注意:下面结果返回的是母串的第几个与子串相匹配,而不是索引值。
由以下代码也可以看出,KMP算法在遇到数据不匹配时,母串的索引i会根据next数组进行跳动,而不是盲目后移一位。这样便高效许多。
在这里插入图片描述

KMP算法的优化

当模式串P[i] ==P[k]时,那么next[i] =next[k],i<k

结束

以上是一些个人理解,有不准确的地方还希望大家多多指导,互相交流,希望对大部分初学者有所帮助。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值