KMP淺顯理解
KMP演算法解釋
1.好的部落格
從頭到尾徹底理解KMP演算法,這是我在搜尋各種部落格之後,解釋的最為詳盡的一篇關於介紹KMP演算法的一篇部落格。自己的理解也算是建立在這篇部落格上。很早很早之前就已經學過了,不過還是忘了好多。現在再拾起來按照自己的理解疏解一遍。
2.KMP緣何而起
目標問題,給你一個文字串S,文字串T求出S中與T相匹配的第一個位置。
我們有樸素的演算法是:
int j=1; int i=1; int s0=strlen(S); int tp=strlen(T); while(i<=s0&&j<=t0) { if(S[i]==T[j]) { i++; j++; } else { i++; j=1; } }
這是最為簡單的暴力演算法,同時我們也會發現,每一次匹配不成功的時候我們都會要去從頭開始匹配。
a | b | c | d | e |
---|---|---|---|---|
a | b | b | d | e |
如上圖所示當abc與abb失配的時候我們會將a再與b去匹配,但是實際上我們早已經知道了第二個字元是b。所以如此顯而效率會較為低下.
3.KMP演算法解釋
Knuth-Morris-Pratt 字串查詢演算法,簡稱為 “KMP演算法”,常用於在一個文字串S內查詢一個模式串P 的出現位置,這個演算法由Donald Knuth、Vaughan Pratt、James H. Morris三人於1977年聯合發表,故取這3人的姓氏命名此演算法。
簡要思路:當我們匹配到S字串中的第i個位置的時候,此時匹配到T字串的第j個字元,此時有i-j+1位置到i位置與T字串中的字元相匹配。
如果第j+1個字元相匹配,接著向下進行匹配即可。
如果第j+1個字元失配,我們就控制S中字元位置不發生改變,將T向後滑動一段位置。(next[j])
我們先來看KMP演算法的程式碼
int j=0; int s0=strlen(S); int t0=strlrn(T); while(i<s0&&j<t0) { if(j==0||S[i]==T[j]) { i++; j++; } else { j=next[j]; } if(j>t0) return i-t0; else return 0; }
程式碼中next即為向右滑動的值。那麼如何去理解這個next值則成了關鍵,自己也被折磨了好久好久。
int j=0;
int i=1;
next[1]=0;
int t0=strlen(T);
while(i<t0)
{
if(j==0||T[j]==T[j])
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
理解如下
A | B | C | D | A | B | C |
---|---|---|---|---|---|---|
A | B | C | D | A | B | D |
A | B | C | D | A | B | D |
---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | 1 | 2 |
在這裡j所表示的含義代表著第j個字元位置前面與T字串從頭開始最大匹配長度。拿上面表格舉例來說,在最後一個位置我們發生了失配問題,D之前與字串T從頭匹配最大長度為2,截至到C為止。所以我們就將字串的位置滑到了C處。 next的值所表示的東西就是j處字元前面的字元與T字串從頭開始匹配的最大長度!!!
但其實截至到現在這個演算法還不算完美,因為我們還會有一種極特殊的情況。
A | A | A | A | A | B |
---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 |
比如上面表格所列處的這種結構當B失配的時候我們右移到了左邊的A的位置,A失配後我們右移到了第四個A的位置,但顯然我們之前已經知道了肯定會失配的結果。
a | b | a | d | e |
---|---|---|---|---|
a | b | a | b | e |
0 | 1 | 1 | 2 | 1 |
b失配移到了第二個位置但這個時候還是b,所以所得結果依然會是失配,明知故作?
實際上是我們求next的過程中有點小疏漏,這個時候應該要能夠滿足如果j==T[next[j]]那麼j=next[next[j]],因為如果T[j]=T[next[j]]則肯定失配,則應該儘量避免這種狀況。
int j=0;
int i=1;
int t0=strlen(T);
while(i<=t0)
{
if(j==0||T[j]==T[i})
{
i++;
j++;
if(T[i]!=T[j])
next[i]=j;
else
{
next[i]=next[j];
}
}
else
{
j=next[j];
}
}
自己目前也理解的不是特別充分,也只能將自己想出來的,大概寫出來成這個樣子了,實在理解不了的話可以採取強制記憶措施,只不過實在不是太好的樣子。
up!up!