KMP算法代码详解

本文详细解析了KMP算法的思想,重点在于如何理解和构建next数组。KMP算法用于解决字符串匹配问题,避免了暴力匹配时不必要的回溯,提高了效率。通过分析匹配失败后的模式串位置,KMP算法利用next数组确定下次匹配的起始位置。文章还指出KMP算法在遇到重复前后缀时可能存在的多余操作,并提出改进next数组计算的方案。
摘要由CSDN通过智能技术生成

在这里插入图片描述
学习了kmp算法,思想其实很好理解,但是代码的实现一直看得很迷糊。看了很多博客,特别是对匹配信息的next数组有很多不同,比如数组有首位有-1的也有0的等。我自己也疏离了一遍,记录一下,方便之后以后回忆。
首先KMP算法主要是用来解决字符串(也叫主串)中的模式串定位问题,比如,有一个主串"abaabaabbabaaabaabbabaab",要找出主串中是否存在子串"abaabbabaab",并返回匹配到的具体位置。
在这里插入图片描述

如果是普通的暴力匹配,应该是将主串和模式串从左到右一个个匹配,如果这个过程中有某个字符不匹配,就跳回去,将模式串向右移动一位。比如
在这里插入图片描述
在匹配到第4位的时候,发现与模式位不匹配,则将模式串向右移动一位继续从头开始匹配。
在这里插入图片描述
再发现注册的第2位字符和模式串的第1位字符不匹配,模式串继续右移
在这里插入图片描述
然后再依次匹配,最终算出结果。这种方式的时间复杂度较高为O(m*n)。
其实观察上面的匹配过程,在第一次主串第4位和模式串第4位匹配不成功的时候,因为前三位其实已经匹配过了。并且模式串第2.3位没有与第1位a匹配的。也就是说,在暴力匹配中的第二次向右平移用主串的第二位b与模式串第一位a,第三次向右平移,用主串的第三位c与模式串第一位a做匹配,是必然失败的。所以可以省略这样的步骤。将模式串第一位a直接与主串刚刚匹配失败的第四位a做匹配。
在这里插入图片描述
在一一匹配后,发现主串的第10位与模式串的第7位不匹配,此时,之前与模式串匹配过的前6位中 abcdab,后缀ab与模式串开头ab是相同的,无须比较,可以将模式串的第三位移到主串不匹配的第10位底下进行比较。
在这里插入图片描述
KMP算法其实就是主串不去移回已经比较过的位置内容,利用已经匹配过的已知信息,在模式串中筛选出合理的位置字符,去与主串的字符做匹配,这样就提高了效率。这一步的时间复杂度就只有O(m)了。但是在匹配失败后怎么知道主串的这个字符要与模式串的哪个字符做匹配呢?这里就需要一张匹配表,一般都喜欢用next数组来表示。索引从0开始,一一对应模式串的字符。对应索引的数组值是匹配失败后,模式串下一个与主串对应字符匹配的字符索引(也可以理解为最长前后缀匹配的位数)。所以求next数组需要遍历模式串,时间复杂度为O(n),总体整个KMP算法的时间复杂度为O(m+n)。

这里说下怎么求next数组吧,具体的理论可以去参考其他的博客,这里就不再详细的介绍了,主要是来记录了解代码的。

首先在已匹配模式串中,如果以i代表模式串的索引,j代表匹配重复子串(最大前后缀)的索引+1。从开头到结束不包括模式串结尾属于前缀,从结尾到开头(不包括开头)叫后缀。比如模式串abcdabd,如果已经匹配了6位,最后第7位d匹配失败,d所对应的前缀都有 a、ab、abc、abcd、abcda,所对应的后缀有b、ab、dab、cdab、bcdab,他们的相同前缀为ab。已经匹配过的字符中,第3位c与匹配失败的第7位d有相同的前缀ab,并且ab是模式串的开头,所以在下一次匹配中,可以跳过ab,直接将第3位c移动到现在匹配失败d的位置。也就是主串对应的字符在匹配d失败后直接就与第3位c做匹配。那么next[6]的值就是c的索引2,也等于最大子串匹配的长度。

因为模式串开头第一个字符是没有前缀的,所以可以设置一个特殊的数字next[0]=-1,从第二个字符开始做匹配,i=1。j=0,因为比较重复子串都是从开头也就是模式串第一个字符做比较的。如果i字符与要比较的j字符相同,那么i、j分别加1,这个位置next[]数组就是对应j的值,所以如果最大重复子串长度为1则j在索引1的位置代表索引0位置的字符已经匹配。如果j位置匹配失败,则j回溯到next[j]位置继续匹配,next[]数组的值本来就是对应位置匹配失败后,应该去匹配的索引位置,前面已经介绍过了。

如果j在索引0的位置还是匹配失败了,就说明对应失败位置前面没有任何重复的子串,那么匹配的模式串不需要再匹配,值为0,再将i索引加1进行下个字符的匹配。而next[0]的值为-1,所以j的值也应该加1,重置为索引0,继续从开头做匹配。

所以大概的求next数组的代码是

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值