包 你 理 解 的KMP算法

18 篇文章 0 订阅
11 篇文章 0 订阅

开头:之前已经看过好几遍 KMP 了,但每次一想就又忘了,可恶!!!
所以今天我到处学习,就写一个 炒 鸡 详 细 的 KMP 的思路过程,这我要再能忘真就白痴了(FLAG高高立起)。

废话不多说,开始我的表演:
两个字符串,text,pattern,
要求:在text中查找pattern。

思路:
KMP算法的精髓和text是没有关系的,精髓在于处理pattern之中。
用i遍历text,j遍历pattern。
我们知道,常规的暴力方法每匹配失败的时候,i总会回到原来的地方+1,于是时间复杂度就变成了O(MN)之高。
而KMP算法则不同,KMP算法的i只会往前进,这都得益于我们对pattern数组的绝妙处理。
重点来了!!
对pattern数组的处理本质上便是找到每个位置对应的相等的最大前后缀。
很绕口,我们来举个栗子!
加入pattern是 a b a b c a b a a
我们分解为
1、a(len = 0)
2、a b(len = 0)
3、a b a(len = 1)
4、a b a b(len = 2)
5、a b a b c(len = 0)
6、a b a b c a(len = 1)
7、a b a b c a b(len = 2)
8、a b a b c a b a (len = 3)
9、a b a b c a b a a(len = 1)

每一段括号里的数字就是该段对应的最大公共前后缀。
我们发现在第七行到第八行,如果我们要让最大公共前后缀加一,我们必须使得新添加的元素等于pattern[len]。
我们只需要用一个prefix数组将它存起来,就快大功告成了。
那么到这里可能就有人问了,可恶,前缀怎么求啊??!
代码配注释:

int i = 1;
    int len = 0;//记录当前比较位置之前的字符串的长度
    while(i<pattern.length()){
        if(pattern.charAt(i) == pattern.charAt(len)){//好理解
            len++;
            prefix[i] = len;
            i++;
        }else{
            if(len>0){
                len = prefix[len-1];//重难点在此,为什么要这样
                //相当于prefix[len]和pattern[i]匹配失败,但是说不定pattern[prefix[len-1]]可能会匹配成功
            }else{
                prefix[i] = 0;
                i++;
            }
        }
    }
}

而这里有个小技巧:
001201231
-100120123
将数组所有元素右移一位,最左边补-1,这将有利于我们后续的操作。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 0 1 2 3 4 5 6 7 8 9 10 11 12 13
例如我们要在 text:a b a b a b a b c a b a a b
;;;;;;;;;;查找pattern:a b a b c a b a a
我们建立的prefix:-1 0 0 1 2 0 1 2 3

用 i 遍历text,j 遍历pattern.

上代码

int j = 0;
int i = 0;
 while(i<text.length()){
     if(text.charAt(i) == pattern.charAt(j)){
         i++;
         j++;
         if(j == pattern.length()) return i-j;
     }else{
         if(j>0){
             j = prefix[j];
         }else{
             j = 0;
             i++;
         }
     }
 }
 return -1;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值