算法_KMP算法学习笔记

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为Knuth——Morris——Pratt操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串P(长度为m)与主串T(长度为n)的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度为O(m+n)。

以下为我理解内容:

  相对暴力的解决方法,KMP在T的[ i-j, i-1 ]与P的[0, j -1]匹配且T[i]!=P[j]时,索引j下一步指向的位置标记为next(j).

  即在KMP算法中,要求next函数满足:T的[ i-next(j), i-1 ]   与  P的[0, next(j) - 1] 是匹配的 。

  又因为T的[ i-j, i-1 ]与P的[0, j -1]匹配,所以next函数应该满足P的[0, next(j) - 1] 与 P的[j - next(j), j -1]是匹配的。即:

  next(j)的含义应该是在P字符串的[ 0, j - 1]中满足前缀字符串和后缀字符串匹配的最大字符串长度。

public static int[] Next(String p) {

    int[] next = new int[p.length];

    next[0] = -1;
    int j = 0;
    int k = -1;

    while (j < p.length() - 1) {
    //由于字符串本身是其自身的前缀子字符串和后缀子字符串,所以对p只计算[0,length - 2]最长字符串中的next函数值

       if (k == -1 || p[j] == p[k]) 
        //j为p的[0,j - 1]子字符串的下一个字符
        //k为p的[0,j - 1]子字符串
           next[++j] = ++k;
       else
           k = next[k];
    }
    return next;

}

next函数的实现:

  由于next(j)为P字符串的[ 0, j - 1]子字符串中满足前缀字符串和后缀字符串匹配的最大字符串长度。所以

  1. 当j==0时,子字符串[ 0, j - 1]不存在,设定next(0) = -1。
  2. 当j==1时,子字符串[ 0, j - 1]是[0, 0],由于字符串本身是其自己的前缀和后缀,所以设定next(1) = 0;
  3. 当j > 1且p[j] == p[k]时,由于子字符串[ 0, k - 1] ( = [ j - k, j - 1] )为子字符串[ 0, j - 1]的最长前后缀匹配字符串,即next(j) = k.所以next( j + 1 ) = k + 1,即子字符串[ 0, j ]的最长前后缀匹配字符串长度为k+1;然后继续去比较p[j + 1] 和 p[k+ 1]是否相等。
  4. 当j > 1时且p[j] != p[k]时,此时需要寻找子字符串[ 0, j ]的最长前后缀匹配字符串。显然,next(j + 1)肯定小于next(j) = k。所以可以转变思路为寻找匹配的字符串[ 0, k - 1 - x]和[ j - k + x + 1, j]。

            又由于next(j) = k

            所以[ j - k , j - 1] = [0, k - 1 ],即[ j - k + x + 1 , j - 1] = [  x + 1, k - 1  ]

            即问题可转换思路为寻找字符串p[0, k]+p[j]的最长前后缀匹配字符串。由如下解释可知,此时next( j + 1 )可通过跳转至步骤3顺序执行进行求解:

            当p[j] == p[k]时,字符串[ 0, j ]的最长前后缀匹配字符串长度next( j + 1 ) = next (k ) + 1

            当p[j] != p[k]时,字符串[ 0, j ]的最长前后缀匹配字符串长度next( j + 1 )求解跳转至第4步继续求解。

 

vector<int> KMPnext(string p){
        int size = p.size();
        vector<int> next(size, 0);
        next[0] = -1;
        int j = 0, k = -1;
        while(j < size -1){
            if(k == -1 || p[j] == p[k])
                next[++j] = ++k;
            else
                k = next[k];
        }
        return next;
    }
    
    
    int strStr(string haystack, string needle) {
        if(needle.empty())
            return 0;
        int m = haystack.size(), n = needle.size();
        if(m < n)
            return -1;
        
        vector<int> next = KMPnext(needle);
        
        for(int i = 0, j= 0; i < m ;){
            if(haystack[i] == needle[j]){
                i++;
                j++;
            }
            if(j == n )
                return i- j;
            
            if ( i < m &&haystack[i] != needle[j]) {
                j?j= next[j] : i++;       
            }
        }
        
        return -1;
    }

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值