文章標題 改進的模式匹配演算法
阿新 • • 發佈:2018-11-11
一.演算法功能:
改進的模式演算法是由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函式值並存入陣列next中
int 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);
}