KMP算法

来源:互联网 发布:读读日报和知乎日报 编辑:程序博客网 时间:2024/06/11 02:59

          KMP算法 匹配时间为Θ(n) ,只用到辅助函数next ,它在Θ(m)时间内根据模式预先计算出来,并且存储在数组next[1.....m]中。数组next使得我们按需要及时有效计算转移函数δ。粗略的说,对任意状态q=0,1,2,....m和任意字符a∈∑ ,next[q]的值包含了与a无关当在计算δ(q,a)的信息。由于next只有m个元素,而δ有m|∑|个值,所以通过预先计算next而不是δ,可以使计算时间减少一个因子。

    关于模式的前缀函数next包含模式与其自身的偏移进行匹配的信息。这些信息可用在朴素的字符串匹配算法中避免对无用偏移进行检测,也可以避免在字符串匹配自动机中,对整个转移函数δ的预先计算。


下面给出KMP算法流程

为了便于理解 ,字符串都从P[1....m],  T[1....n];

i初始值为0

j初始值为0

假设现在文本串T匹配到 i 位置,模式串P匹配到 j 位置

     (1) 若 j>0  且T[ i+1 ] !=  P[ j+1 ],则 T [  i-j+1,....i ] == P[ 1....j ]肯定是成立的,根据这个条件,如果我们能找到一个k<j 值,使得 T[ i-k+1.....i] == P[ 1.....k ],那么我们可以令j=k ,再  回到(1)步,判断 T[i+1]==P[j+1],其实k值就是当模式串指在 j 时,P[1.....k]  即是]P[  1.....j  ]的前缀也是T [  i-j+1,......i ]的后缀 ,即 P[1....k-1 ]  ⊃T [  i-j,......i ]  求最大k值    。又因为 T [  i-j+1,......i ]==P[ 1......j  ] 我们可以写成 P[ 1....k ]  ⊃P [ 1....j ],因此我们可以用一个next数组来表示k  ,k=next[j];

     (2) 若 T[ i+1 ]==P[ j+1 ] 则i++,j++,继续匹配下一个字符

     (3) 若j==strlen(P)则匹配成功  j=next[j]

下面用伪代码表示KMP算法

 KMP_Matcher(char *T,char*P){n=T.length;m=L=P.length;int* next = Compute_Prefix_Function(P);j=0;for(int i=0;i<n;i++){while(j>0&&P[j+1]!=T[i+1])    j=next[j];if(P[j+1]==T[i+1]){j+=1;}if(j==m){cout<<"Pattern occurs whit shift "<<i-m<<endl;j=next[j];}}}


求next数组 即next[q]是Pq的真后缀P的最长前缀长度

Compute_Prefix_Function(char*P){int m=P.length;int *next = new int[m+1];next[1]=0;    //Pq的真后缀P的最长缀长度 int k=0;for(int i=2;i<=m;i++){    //i个字符以匹配成功 while(k>0&&P[k+1]!=P[i])  //没有匹配成功     k=next[k];if(P[k+1]==P[i])   k+=1;next[i]=k;}return next;}


下面给出完成的程序

为了便于理解,模式字符串是P[1....m],文本字符串是T[1....m]

#include<iostream>#include<string.h>using namespace std;int* Compute_Prefix_Function(char*P){int m=strlen(P)-1;int *next = new int[m+1];next[1]=0;    int k=0;for(int i=2;i<=m;i++){    while(k>0&&P[k+1]!=P[i])  //没有匹配成功 k=next[k];if(P[k+1]==P[i])k+=1;next[i]=k;}for(int i=1;i<=m;i++)cout<<next[i]<<endl;return next;}void KMP_Matcher(char *T,char*P){int n=strlen(T)-1;int m=strlen(P)-1;int* next = Compute_Prefix_Function(P);int j=0;for(int i=0;i<n;i++){while(j>0&&P[j+1]!=T[i+1])j=next[j];if(P[j+1]==T[i+1]){j+=1;}if(j==m){cout<<i+1<<endl;cout<<"Pattern occurs start whit shift "<<i+1-m<<endl;j=next[j];}}}int main(){char T[]="adfgjhabcabcdaderdfgfdg"; char P[15]="hcabcdaderd";KMP_Matcher(T,P);return 0;} 

             KMP的next 数组:当模式串中的某个字符跟文本串中的某个字符匹配失配时,模式串下一步应该跳到哪个位置。如模式串中在j+1处的字符跟文本串在i +1处的字符匹配失配时,下一步用next [j] +1处的字符继续跟文本串i +1处的字符匹配,相当于模式串向右移动 j - next[j] 位.

    我们发现如果某个字符匹配成功,则i++、j++;如果匹配失配,i不变,模式串会跳过匹配过的next [j]个字符j=next[j]。整个算法最坏的情况是,当模式串首字符位于n-m+1的位置时才匹配成功,算法结束。
    所以,如果文本串的长度为n,模式串的长度为m,那么匹配过程的时间复杂度为O(n),算上计算next的O(m)时间,KMP的整体时间复杂度为O(m + n)

1 0