字串匹配的KMP演算法---計算失配函式
關於理解計算失配函式的一點小心得。
首先,感謝Jake Boxer的文章給我的幫助。
在jake的文章裡面,說的,字首和字尾是理解的關鍵。請先閱讀以下jake的文章(不然可能不好理解)。
正題:假設字串 P 的長度是 m。我們給定 P 的失配函式為 failure[m],failure[0] = 0。
假設 0<= j < m ,如果我們知道了由 failure[j-1] 推匯出 failure[j] 的公式的話,我們就很容易求出失配函數了。
關於 failure[j-1] 與failure[j]的關係,就要考慮到字首和字尾的問題了。jake的文章中提到的,其實失配函式裡面儲存是與當前子字串的字尾相匹配的當前的子字串的最大字首的長度。jake的原文是:The length of the longest proper prefix in the (sub)pattern that matches a proper suffix in the same (sub)pattern.
比如 字串 P = “abababca” ,在 j = 1的時候,子字串是 “ab” ,j = 2 的時候 子字串是 “ aba” , j = 3 的時候子字串是 “abab”。
那麼我們可以看出 failure[1] = 0 ,failure[2] = 1, failure[3] = 2 。
現在考慮 failure[j-1] 已知,表示我們知道 j-1 長度的子字串的最大字首(其實,是與字尾相匹配的最大字首,後面都省略為最大字首),那麼 j 長度的子字串的最大字首。
我們可以這樣來考慮, j-1 的最大字首 後面的一個字元是不是與 j的字元相同,如果相同,那好,這就是j 的最大字首了。如果不同,我們就縮短最大字首,再匹配,這裡要通過 i = failure[ failure[j-1] ] 的方式來獲取更小的最大字首,然後再次比較 i+1 處的字元是不是與 j相同。重複此操作。
failure[j-1] 與 failure[j] 的關係也就是:
a. 當j=0是,failure[j] = 0 ,
b. 當P(j) = P(failure[j-1]+1)時,failure[j] = failure[j-1]+1,
c.當j> 0 且 P(j) != P(failure[j-1]+1)時,迭代查詢 failure[failure[j-1]] +1 位置的字元與 P(j) 是否相同,直至出現a或者b步的情況出現。
下面是我自己實現的程式碼:
void fail(char* pattern){ int m = strlen(pattern); cout<<"m=="<<m<<endl; int* failure = new int[m]; int i = 0; failure[0] = 0; for(int j=1;j<m;j++){ i = failure[j-1]; while(i>0 && (pattern[i] != pattern[j])){ i = failure[i-1]; } cout<<"i=="<<i<<"--j="<<j<<endl; cout<<"pat[i]=="<<pattern[i]<<"--pat[j]=="<<pattern[j]<<endl; cout<<"---------------"<<endl; if(pattern[i]==pattern[j]){ failure[j] = i+1; } else{ failure[j] = 0; } } for(int k=0;k<m;k++){ cout<<failure[k]<<" "; } }
補上KMP的匹配演算法
int pmatch(char* string,char* pat){
int n = strlen(string);
int m = strlen(pat);
int* failure = fail(pat);
int i = 0;
int j=0;
while(i<n && j<m){
if(string[i]==pat[j]){
i++;
j++;
}
else{
while(j>0 && string[i] != pat[failure[j-1]+1]){
j = failure[j-1];
}
if(string[i] == pat[j]){
j++;
}
i++;
}
}
if(j==m){
return i-j;
}
return -1;
}