KMP算法

应用

串的模式匹配:在主串中确定模式串的位置

主串S:S[0]、S[1]、……、S[n-1],长度为n

模式串P:P[0]、P[1]、……、P[m-1],长度为m

next数组:根据模式串的性质生成的,next[i]=k 表示:当模式串中位置i处的字符不匹配时,需要跳转至位置k处进行比较

如何确定next数组?

若next[i] = k,则有:P[0]P[1]……P[k-1] = P[i-k]P[i-k+1]……P[i-1]

我们从前向后推,next[0] = -1,表示当与模式串的位置0处的字符不匹配时,需要将主串向后移动1位,

对于已经确定的 next[i] = k ,推导next[i+1]

(1) 若P[k] == P[i],则有:P[0]P[1]……P[k-1]P[k] = P[i-k]P[i-k+1]……P[i-1]P[i],得next[i+1]=k+1

(2)若P[k] != P[i],即P[0]P[1]……P[k-1]P[k] != P[i-k]P[i-k+1]……P[i-1]P[i],可以看成前缀P[0]P[1]……P[k-1]P[k]在后缀P[i-k]P[i-k+1]……P[i-1]P[i]上匹配,当P[k]与P[i]不匹配时,k则应回溯至next[k]继续进行匹配

为什么是k=next[k]?

假设next[k]=c,则有:P[0]P[1]……P[c-1] = P[k-c]P[k-c+1]……P[k-1]

因为next[i]=k,有:P[0]P[1]……P[k-1] = P[i-k]P[i-k+1]……P[i-1]

得:P[0]P[1]……P[c-1] = P[i-c]P[i-c+1]……P[i-1]

若P[c] == P[i],则有P[0]P[1]……P[c-1]P[c] = P[i-c]P[i-c+1]……P[i-1]P[i],得next[i+1]=c+1

next数组的优化

考虑模式串P="aaab",可得next数组如下第二行,由实际可知,当P[2]不匹配时,P[1]、P[0]也必定不匹配,也就没有必要再次进行比较,改进方法是判断P[i]与P[next[i]]是否相同,若相同则next[i] = next[next[i]],改进后的next数组如下第三行所示

j0123
next[j]-1012
next[j]'-1-1-12

进行匹配

主串一直向前走,模式串根据next数组确定不匹配时的位置,适当后退。

当模式串的位置为-1时表示主串与模式串将要进行新的匹配,主串位置+1,模式串从头开始进行匹配

C++实现

#include <iostream>
#include <string>
using namespace std;
void getNext(string pattern, int* next){
    int length = pattern.length();
    int i=0,j=-1;
    next[0]=-1;
    while (i<length-1){
        if( -1==j || pattern[j] == pattern[i] ){
            ++i;++j;
            if(pattern[i] != pattern[j])
                next[i]=j;
            else
                next[i]=next[j];
        }else
            j=next[j];
    }
}
int KMP(string s,string pattern,int* next){
    int sLength = s.length();
    int pLength = pattern.length();
    int sIndex=0,pIndex=0;
    while ( sIndex<sLength && pIndex<pLength ){
        if( -1==pIndex || s[sIndex] == pattern[pIndex]){
            ++sIndex;++pIndex;
        }else
            pIndex = next[pIndex];
    }
    if(pIndex>=pLength)
        return sIndex-pIndex;
    return -1;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值