1. 程式人生 > >文章標題 改進的模式匹配演算法

文章標題 改進的模式匹配演算法

一.演算法功能:
改進的模式演算法是由Knuth,Morris和Pratt等人共同提出的,所以稱為Knuth-Morris-Pratt演算法,簡稱KMP演算法。KMP演算法是字串模式匹配中的經典演算法,在起匹配過程中,主串指標不回溯,從而提高了演算法效能。

二.演算法思想

  1. 在匹配過程中,如果出現不匹配的情況(當前模式串不匹配字元假設為t[i]),首先從已匹配結果計算出目標串S的第i個字元應該與模式串T中哪個字元在比較時出現了不配的情況,即保證在目標串指標不回溯的前提下,確定模式串中新的比較起點。
      設主串是S=’S1S2.....Sn',模式串為T=T1T2.....Tm;
  2.根據模式串T自身的規律和已知當前的位置j,可以歸納出計算模式串新的比較起點k的表示式。令k=next[j];
           next[j]=0  (當j=1)
           next[j]=max{k|1<k<j且'T1T2.....Tk-1'='Tj-k+1Tj-k+2......Tj-1';
           next[j]=1; 其他情況
           需要說明的是,next[j]中next[0]和next[1]的取值是固定的。
           *匹配規則:*
           (1)當首字母出現不匹配的時候,目標串的指標後移一位,然後在從改位與模式串的第一個字元開始匹配。假定next[0]=-1;
           (2)失配位置j所對應的next[j]的值為接下來要匹配的模式串的字元的索引,也就是說,出現不匹配的時候,模式串的索引指標要回溯到next[j]所對應的位置,而目標串索引指標不回溯。
  3.舉個例子說明:
      例如,現有目標串S='cabdabaabcabaabadcb',模式串T='abaaba'
    1).先把模式串T中可能的失配點j多對應的next[j]計算出來(*由上面的next[j]計算公式*)

      j=1時,next[1]=0;
      j=2時,next[2]=1;
      j=3時,首先,1<k<j,所以k=2;   但是'T1'(此處T1為a)不等於'T2'(此處T2為b),所以next[3]=1;
      j=4時,k={2,3},k=2時,'T1'='T3'(此處T1等於a,T2等於a)
                                 k=3時,  'T1T2'不等於'T2T3'(此處T1T2是ab,T2T3是ba)
                          因此next[4]=max{k|1<k<j且 'T1T2.....Tk-1'='Tj-k+1Tj-k+2......Tj-1'}=2;
      以此類推
      j=5時,next[5]=2;
      j=6時,next[6]=3;

   2).計算完next[j]的值,接下來開始匹配
       *第一次匹配*:S='*c*abdabaabcabaabadcb'
                          T='*a*baaba'
                 設兩個引數i和j,i代表目標串的指標索引位置,j代表模式串指標索引位置,開始時,i=1,j=1;  第一個字元都不匹配,next[j]=next[1]=0;
          *第二趟匹配*:目標串指標加1,i=2,j=1;
                            S='c*abd*abaabcabaabadcb'
                            T='*aba*aba'
                            匹配失敗時,i=4,j=3;next[j]=1(當j=3時)
          *第三趟匹配*:目標串指標不變,j=1.就是從i=4,j=1開始匹配(此處的原因是上文中的匹配規則)
                            S='cab*d*abaabcabaabadcb'
                            T='*a*baaba'
                           匹配失敗時i=4,j=1,next[j]=0(當j=1時)
         *第四次匹配*:目標串指標加1,模式串指標指向第一個字元,也就是j=1,從i=5,j=1開始匹配。
                           S='cabd*abaabc*abaabadcb'
                           T='*abaaba*'
                           失配時i=10,j=6,next[j]=3(j=6)
          *第五次匹配*:目標串不變,從i=10,j=3,開始匹配,
                            S='cabdabaab*c*abaabadcb'
                            T='ab*a*aba'
                            失配時,i=10,j=3,next[j]=1(j=3)
          *第六次匹配*:目標指標不變,從i=10,j=1,開始匹配,
                              S='cabdabaab*c*abaabadcb'
                              T='*a*baaba'
                               失配時i=10,j=1,next[j]=0;
           *第七次匹配*:目標指標加1,j=1,從i=11,j=1開始匹配
                              S='cabdabaabc*abaaba*dcb'
                              T='*abaaba*'  匹配成功,返回模式串在目標串中的位置11.
            以上匹配過程看起來挺麻煩的,只要一步步慢慢來,是很容易理解的;

三.下面是具體的程式碼實現:

#include<stdio.h>

#define MAXL 255
#define OK 1
#define OVERFLOW -1

typedef unsigned char SString[MAXL + 1];

void strAssign(SString &T, char *s)
//用字元陣列s給串T賦值.
{
    int i = 0;
    T[0] = 0;//0號單元儲存串長.
    for (; s[i]; i++)
    {
        T[i + 1] = s[i];
    }
    T[0] = i;
}

void get_next(SString T, int
next[]) { //求模式串T的next函式值並存入陣列nextint i = 1, j = 0; next[1] = 0; while (i < T[0]) { if (j == 0 || T[i] == T[j]) { i++; j++; next[i] = j; } else j = next[j]; } } int Index_KMP(SString S, SString T) { //求子串T在主串S中可以匹配的位置,不匹配返回0
int i = 1, j = 1; int next[MAXL]; get_next(T, next); while (i <= S[0] && j <= T[0]) { if (j == 0 || S[i] == T[j]) { i++; j++; } else j = next[j]; } if (j > T[0]) return i - T[0]; else return 0; } void main() { int pos; SString T, S; char char_a[100], char_b[100]; printf("請輸入主串A:"); gets_s(char_a); printf("%s\n", char_a); printf("請輸入主串B:"); gets_s(char_b); printf("%s\n", char_b); strAssign(T, char_a); strAssign(S, char_b); printf("賦值成功!\n"); pos = Index_KMP(T, S); if (pos) { printf("主串 T=%s 的子串 S=%s 在第%d個位置開始匹配。", char_a, char_b, pos); } else printf("主串 T=%s 和子串 S=%s 不匹配", char_a, char_b); }