Knuth Morris Pratt (KMP)算法的证明及实现代码

    在串的模式匹配中,最简单的BF(Brute-Force,布鲁特-福斯)算法,由于需要回溯,最坏的情况下时间复杂度为O(m*n).其中m,n分别表示主串和模式串的长度。
    由D.E.Knuth, J.H.Morris和V.R.Pratt三人提出的KMP算法,由于不需要回溯,其时间复杂度可以降到O(m+n)。其中m,n意义同上。
    下面给出KMP算法的一个证明。
    讨论一般情形,设主串S=“S0 S1 S2 ... Sn-1”, 模式T=“T0 T1 T2 ... Tm-1”.在进行第 i 趟匹配时,出现以下情况:
    S: S0   S1   ...   Si-j   Si-j+1   ...   Si-1   Si   Si+1   ...   Sn-1
                        ||    ||            ||    `|
                        T0   T1      ...   Tj-1  Tj   Tj+1   ...   Tm-1

即: T0 T1 ... Tj-1 ==  Si-j Si-j+1 ... Si-1,  且 Si != Tj.     ---(1)

如果在模式T中, 有: T0 T1 ... Tj-2 != T1 T2 ... Tj-1,  ---(2)
则由(1)得:
             T1 T2 ... Tj-1 == Si-j+1  Si-j+2 ...  Si-1.   ---(3)
将(3)代入(2),则马上有:
                   T0 T1 ...  Tj-2 !=  Si-j+1  Si-j+2 ...   Si-1.
则显然有:
                   T0 T1 ...  Tj-2  Tj-1 != Si-j+1 Si-j+1 ... Si-1 Si.
由上述证明可知:回溯到Si-j+1开始进行匹配必然失败。  也就是说,回溯到Si-j+1进行匹配可以不做。那么,考虑回溯到Si-j+2的情况:
    从前面的推理可知,如果有
                   T0  T1  ...   Tj-2  !=   T2  T3 ...  Tj
则必然有:
                   T0  T1  ...    Tj-2  !=   Si-j+2   Si-j+3   ...  Si  .
这样的比较仍然会失败。依此类推,直到对于某一个值k,使得:
      T0  T1 ...   Tk-2   !=   Tj-k+1  Tj-k+2  ...  Tj-1  且   T0  T1  ...  Tk-1 ==  Tj-k  Tj-k+1 ...  Tj-1,
才会有:
        Tj-k   Tj-k+1  ...   Tj-1  ==   Si-k   Si-k+1  ...  Si-1  ==   T0  T1 ...  Tk-1.
这说明下一次可直接比较 Si 和 Tk,这样,可以直接把第 i 趟比较失败时的模式 T 从当前位置右移 j-k 位。而这里的 k 即为next(j)。
关于next(j)的求法不再证明。求next(j)的算法其实就是上述算法的又一次运用,只不过这时主串和模式串相同。
下面是KMP算法的C实现。假设串的下标从0开始。

const int MAXSIZE = 100;
void getNext(char *T, int next[]){         //求next
    int j = 0, k = -1;
    next[0] = -1;
    while(j < strlen(T)){
       if( (j == -1) || (T[j] == T[k]) ){
     j++; k++;
     next[j] = k;
       }else
          k = next[k];
    }
}
    
int KMP(cahr *S, char *T){
    int next[MAXSIZE], i = 0, j = 0, v;
    getNext(T, next);
    while( (i < strlen(S)) && (j < strlen(T)) ){
         if( j == -1 || S[i] == T[j]){
             i++;
             j++;
         }else
             j = next[j];
    }
    if(j > strlen(T))
         return (i - strlen(T));
    else
         return -1;
}         




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值