深度分析kmp算法,从原理到实现(看了必会,简单直观)

kmp算法,常用于匹配一个字符串是否是另一个字符串的子串,不同于暴力搜索kmp算法的时间复杂度为O(m+n),网上好多对于该算法的讲解代码,但发现有木有,几乎所有的代码全是一模一样,根本没几个是自己写的,给的都是书上标准优化后的代码,直接抄下来,理解困难,原理讲解不清,含糊其词,因此本人自写一遍关于kmp算法的博客,并讲解该kmp算法的原理,以及为什么需要next数组,并证明每次最优移动次数就是next[j]的值,最后依据讲解的原理和思路给出自己的Kmp算法的具体实现,请尊重个人劳动成果,转载请注明出处。

 首先请明白next数组是什么,术语讲就是字符串的前缀和后缀的最长公共长度,比如ABCEFGHRHHABC则最长公共长度就是ABC,使用术语可能会妨碍大家理解,这里我使用图形象描述。如图所示字符串ABCEFGHRHHABC的最长公共前缀是多少呢?很简答,你只需要讲该字符串数组右移,舍得末尾(ABC)能跟开头(ABC)重合的最大长度就是前缀和子缀的最大公共长度(描述起来很麻烦,画图好理解,下定义要描述起来还是麻烦啊)

明白了这个之后,现在我们来证明在匹配失败后最优匹配位置为next[j]的值,怎么证明呢?很简单的证明方法,反正法!!,假设我们移动距离不是next[j]的值,使得移动后也匹配结果,具体看下图分析,我们用X变量代表以匹配的字符变量可以代表任意字符,Y变量代表未匹配的字符,也可以表示任意字符,看下图证明:

 

 

接下来就是next数组的求法呢?这个可以用动态规划做,假设k=next[t-1],我们怎么求next[t]呢,

分两种情况,若p[t]==p[k],是不是直接到加到后面,就行了,符合next的定义,因此next[t]=k+1;

那如果p[t] !=p[k]呢?怎么求,首先如果p[t]!=p[k]说明不能直接加,那怎样才能加入?,才能符合next[t]的定义呢?

根本就是要找到,j前面有几个数,可以加入,很间单啊,看我下面的图分析,包你搞懂。

//具体 算法如下

public static int[] getMyNext(String ps) {
    char[] p = ps.toCharArray();
    int[] next = new int[p.length];
    next[0] = 0; //初始化第0位为0
    int k;


    for (int t = 1; t < p.length; t++) {  
        k = next[t - 1]; //获取前一位的next
        //  System.out.println(p[k]);
        if (p[t] == p[k]) {
            next[t] = k + 1;
        } else { //不相等则需要递归,这里使用while循环使用
            while (true) {
                int temp = next[k];
                if (p[t] == p[temp]) {
                    next[t] = temp + 1;
                    break;
                }
                if (temp <= 0) {
                    next[t] = 0;
                    break;
                }

            }


        }
    }
//数组整体右移一位,为什么要右移?因为在kmp算法中我们在加入第j个字符的时候用的是第j-1的next数组,所以求玩后整体右移一位,
    moveToRight(next, 1);
   next[0]=-1;//并且我们将0位置为-1,主要是为了标识,匹配到0的情况
    return next;
}
public static void moveToRight(int[] array, int index) {

    System.arraycopy(array, 0, array, index, array.length - 1);
    System.out.println(array);
}

//既然有了next数组kmp算法也不再话下了

/**
 * 我写的kmp算法
 *
 * @param ts
 * @param ps
 * @return
 */
public static int MyKMP(String ts, String ps) {

    char[] t = ts.toCharArray();

    char[] p = ps.toCharArray();

    int i = 0; // 主串的位置

    int j = 0; // 模式串的位置

    int[] next = getMyNext(ps);


    while (i < t.length && j < p.length) {

        if (t[i] == p[j]) { //

            i++;
            j++;


        } else {

            // i不需要回溯了

            // i = i - j + 1;

            j = next[j]; // j回到指定位置
            if (j == 0) { //j==0 了 只能i右移重新从0开始匹配了
                i++;
            }

        }

    }

    if (j == p.length) {

        return i - j;

    } else {

        return -1;

    }
}

 

 

 

 

 

 

 

 

 

 

 

 

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值