KMP算法通俗讲解以及Java实现

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出,所以被称为看毛片算法(我可不知道毛片是什么东西)。
其核心思想就是利用模式串的局部对称,在每一次匹配失败后,达到一种玄幻的跳跃,极大加快匹配速度。玄幻如下:
(注:上串为主串,下串为模式串)
在这里插入图片描述
当主串与模式串匹配到i=j=4时,发现不匹配,KMP算法下发生了恐怖事件,匹配状态直接变为下图。
在这里插入图片描述
我们朴素的字符串匹配方法,匹配到i=j=4时,下一步操作应该是把j置为0,i置为1,继续进行比较。而KMP这一步直接将j置为2,与i=4进行比较。(想一想,为什么?)

这里利用了模式串的首尾子串的对称性。在j=4的b之前的字符串,头两个字符和最后两个字符相同,利用首尾部分的重叠,将头部分覆盖尾部重叠部分,继续之后的比较,这样就跳过了没有必要的比较。(想一想,为什么?)

有的小伙子可能会问:凭啥中间的就没有比较的必要了?万一中间有和头部重合的部分,但是主串和模式串却匹配了,而且并不属于尾部重叠串,你这个部分就有可能出错啊!
回答当然是:不可能!因为重叠若没有延续到结尾,则必然有不相等的部分,看如下栗子。
在这里插入图片描述
对于模式串来说,第3、4个字符和第1、2个字符是一样的,但当匹配到d!=a时,将字符串“后移”,使其变为如下情况:

在这里插入图片描述
显然是肯定不会匹配的,因为若匹配了,第3、4个ab应属于尾部重叠串的一部分。
所以可以再来康康下面这种情况
在这里插入图片描述
下一步操作使情况变为
在这里插入图片描述
(想一想,为什么?)
到此KMP算法的大致思想我们应该知道了,感觉代码应该不难写。上代码:

/**
 * KMP工具类
 */
public class KMP {
    /**
     * 用于计算匹配的位置(从头到尾)
     * @param str 主串
     * @param sub 模式串
     * @return
     */
    public static int kmp(String str, String sub) {
        int i = 0,j = 0;
        int[] next = getNext(sub);
        while (i < str.length() && j < sub.length()){
            if(j == -1 || str.charAt(i) == sub.charAt(j)){
                i++;
                j++;
            }else {
                j = next[j];
            }
        }
        if(j == sub.length())
            return i-j;
        else
            return -1;
    }

    /**
     * 用于生成部分匹配表next
     * @param sub 模式串
     * @return
     */
    private static int[] getNext(String sub) {
        int[] next = new int[sub.length()+1];
        int i = 0, j = -1;
        next[0] = -1;
        while(i<sub.length()){
            if (j==-1 || sub.charAt(i) == sub.charAt(j)){
                next[++i] = ++j;
            }else {
                j = next[j];
            }
        }
        return next;
    }
}

当然感觉永远是错的,next又是个what?你没有想错,next就是模式串当前匹配失败后应该“覆盖”他的字符编号。比如下图,c所属的字符编号就为6,记为next[8]=6(c是第7个元素)。
在这里插入图片描述
求这个next数组的代码也很匪夷所思。我来讲讲大概过程,看起来应该就一目了然了。
在这里插入图片描述
若要求next[13],可知next[12]=6。此时j=6,这就表明1-5和7-11元素重叠。
在这里插入图片描述
此时比较sub[6]和sub[12]
若相等,则next[13]=j+1=7。
若不相等,则使j=next[6]。
若next[6]=3,则1、2和4、5处元素重叠,并且由next[12]=6,可得1、2处元素和10、11处元素重叠,此时比较sub[3]和sub[12]
若相等,则next[13]=j+1=4
若不相等,且next[3]=0,就变为首字符和尾字符的比较,若相同,则next[13]=2,若不同,next[13]=1。
在这里插入图片描述
最后写个main

public class Main {
    public static void main(String[] args){
        int res = KMP.kmp("abababbcabca","ababbca");
        if (res==-1){
            System.out.println("匹配失败");
        }else {
            System.out.println("匹配位置为"+res);
        }
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值