1,## KMP算法的來源? ##
由於樸素匹配算法非常低效,需要挨個遍歷主串,所以有三位前輩,D.E.Knuth,J.H.Morris,和V.R.Pratt發表了一個模式匹配算法,可以大大避免重複遍歷的情況,我們稱這爲克努特–莫里斯–普拉特算法,簡稱KMP算法。
2,## 何爲KMP算法? ##
KMP算法在匹配過程中發生失配時,並不是簡單的從原始串的下一個字符重新開始匹配,而是根據匹配過程中所得到的信息跳過不必要的匹配,從而達到高效的匹配算法。
下圖主串爲S串abcabcabcd,匹配串爲T串abcabcd
當第一次匹配到S[6]!=T[6]時,主串不回溯,匹配串沒有回溯到回0位置,而是回溯到下標爲3的位置,繼續匹配,直到匹配完成。
那爲何匹配串會回溯到下標爲3的位置呢?實際上KMP匹配算法實現了一個next數組,匹配串回溯到next數組對應下標所存儲的位置,此時next[6] = 3。
3,## next數組的定義及計算 ##
(1),next數組的定義
next數組:存儲的是匹配串匹配失敗時回溯的位置
對於匹配串而言,滿足匹配串中存在兩個真子串相等,一個從0位置開始,一個以j-1位置結束,j爲當前訪問匹配串的下標,滿足的公式是p0…pk-1 == pj-k..pj-1;k爲next數組j下標所保存的值。
(2),next數組的計算
令next[0] = -1;next[1] = 0;next[j] = fun(next[j-1]);
根據p0…pk-1 = pj-k…pj-1公式可以求的,當next[j] = k;
next[j+1] 的值 就可以分爲兩種情況
if(pk == pj) next[j+1] = k+1; p0…pk = pj-k…pj
此時的pk就相當於sub[k],而pj也就相當於sub[j]
if(pk != pj) k = next[k] ,然後在判斷pk與pj是否相等
如果pk與pj一直不相等,直到k=-1,那就讓它執行next[j+1] = 0; 無相等的匹配串,直接回溯到匹配串的第一位
p[j] != p[k] 直到 k = -1,不能在回退了。
4,## next數組代碼實現 ##
思路 : 用next[j]求next[j+1],依此類推
void get_next(char *sub,int *next,int length)
{
assert(sub != NULL && next != NULL);
if(length < 1)
return;
next[0] = -1;
next[1] = 0;
int j = 1; 通過next[j] 計算 next[j+1]
int k = 0;
while(j < length - 1)
{
if(k == -1 || sub[k] == sub[j]) //如果相等,next數組++,如果k==-1,讓sub[j+1] = 0;回溯到0位置
{
next[++j] = ++k;
}
else
{
k = next[k];
}
}
}
5,## KMP算法代碼實現 ##
思路 : KMP匹配算法實現了一個next數組,
在匹配的時候主串不回溯,匹配串回溯到next數組
對應下標所存儲的位置,然後繼續進行匹配,
直到匹配完畢。
int KMP(const char *str, const char* sub)
{
if(str == NULL || sub == NULL)
return -1;
int str_len = strlen(str);
int sub_len = strlen(sub);
if(sub_len > str_len || sub_len == 0)
return -1;
int next[255];
get_next(sub,next,sub_len); //得到next數組
int i = 0;
int j = 0;
while(i < str_len && j < sub_len)
{
//當j等於-1時,說明不匹配,主串後移,匹配串j++到0位置,繼續匹配,直到匹配完畢
if(j == -1 || str[i] == sub[j]) //相等同時後移
{
++i;
++j
}
else
{
j = next[j]; //匹配串回溯到next數組所存儲的位置
}
}
if(j >= sub_len)
return i - j;
return -1;
}
//測試一下 代碼
int main()
{
char *str = "ababababc";
char *sub = "ababc";
int k = KMP(str,sub);
printf("%d\n",k);
return 0;
}
結果顯示:從第四位開始匹配上了,例子很多,我這就只舉一個啦,接下來讓我看看KMP的時間複雜度。
6,## KMP算法的時間複雜度 ##
對於主串的長度爲m,匹配串的長度爲n,進行匹配,KMP的時間複雜度爲O(m+n),相比於BF算法時間複雜度O(m*n)效率已經是提高的很多了。
對於KMP算法的優化,可以選擇去優化next數組來實現,有興趣的碼友可以自己實現一下。