关于KMP算法中获取Next数组方法简化的一个尝试(Java代码实现)

字符串匹配KMP算法 (关于部分匹配数组的获取! 已发现方法有误)

2020/11/24 更新:已经知道自己错在哪里了,这种方法确实是不对的
对于aabaaa 这个字符串 PMT应该是010122而我的方法就变成了010121
留作纪念吧哈哈哈哈


KMP算法的两种实现方式:Next数组与PMT表,这两天一直纠结Next数组的获取方式,现在把自己的思路写在这里,方便以后复习,也欢迎大家指出错误!这里主要是讨论部分匹配数组或是Next数组的获取的一个细节,关于算法的内容这篇文章写的很详细啦:
https://www.cnblogs.com/zzuuoo666/p/9028287.html
KMP算法的实质:就是在主串匹配模式串失败的时候,不用从头再开始,而是根据已有的信息,回溯到对应的位置

两种方式(本质上都是一样的,只是使用方式有所不同):

①next 数组: next数组每个坐标代表了,这个坐标之前的字符串的前后缀最大公共子串长度

②部分匹配数组: 它代表了从第一个到这一个所有字符串的前后缀最大公共子串长度

字符串数组 A B C D A B C D A B D A
位置 0 1 2 3 4 5 6 7 8 9 10 11
next数组 -1 0 0 0 0 1 2 3 4 5 6 0
部分匹配表(PMT) 0 0 0 0 1 2 3 4 5 6 0 1

从上表可以看出,如果能够获取部分匹配数组,next数组就是把部分匹配数组向右移动一位,并把next[0]=1

next[i]就代表了,回溯的时候要回溯的位置即i=next[i]

而部分匹配数组中回溯的值是i=PMT[i -1]

在获取数组中的一个注意点,以PMT为例,很多教程在PMT[j] != PMT[i]的时候, j= PMT[j-1] ,这是KMP的核心,但是!!!,在求子串的PMT或是NEXT数组的时候,我认为是没有必要的 以PMT数组为例,相当于上表中匹配到第10位置(从0开始),

ABCDABCDAB已经是匹配完成的了,且到最后一个B的时候,这个串的前后缀公共长度是6

  • 分别是前缀:ABCDABCDAB后缀ABCDABCDAB,所以接下来匹配的是“新加入的”PMT[10] 与 C 很明显 PMT[10] = D != C

  • 一般情况下, 此时j = PMT[10 - 1]=PMT[9] = 6 实际上是比较ABCDABC 和ABCDABD是否相等,因为ABCDABCDAB后缀ABCDABCDAB已经相等了!只要这个新的相等,那么长度就继续增加,如果不等,那么尝试看看**有没有更短的前缀能匹配 ** 而而PMT[6] = C !=D所以这个串不可能再延长了,回溯! j = PMT[6-1]

  • 那么再回溯 j = PMT[6-1]=PMT[5] = 2 此时实际上是找到一个更段的公共部分 ABCDABCDABD 比较第一个AB后面的字符 和当前AB后面的D是否相等 ,如果相等 则 PMT[10] = 3 ,否则再回溯 而 PMT[2] = C != D继续回溯 j = PMT[2-1] = PMT[1] = 0 PMT[0]= A 此时就是看新加入的 D 能否和 最开始的A形成公共部分

此时比较 A 与 D 很显然不相等 所以 PMT[10]= 0

从上述的推断中,可以看出,除了回到字符串首位的情况,其余的回溯过程,取的都是同样的值!,也就是说,除了回溯到句首的情况,只要出现回溯的情况,每次都是比较相同的字符。这是由于PMT数组的性质决定的,也正因为这个特性 ,在匹配主串的时候能够提高效率,但是在模式串自匹配的时候就显得很多余。

一旦出现不匹配,且可以回溯的情况,其实只要考虑两种

  • 如果当前的字符j和 str[0] 相等,则长度为PMT[j]=1
  • 如果不等,长度为0 PMT[j] = 0

而在匹配主串的时候,则要按照j=PMT[j-1]的规则,否则就变成了暴力匹配!对于next数组也是一样的情况,只是部分匹配表更加直观一点!!

实际上这两个的本质是一样的!
分别用这两种方式实现KMP:

//第一种,采用PMT数组,我认为是比较容易理解和记忆的一种
public class KMP {
   
    public static void main(String[] args) {
   
        String str1 = "BBC A ABCDABCDABD";
        String str = "ABCDABD";
        int[] kmpNext = getKMPNext(str);
        System.out.println(Arrays.to
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值