KMP算法的next[]數組通俗解釋
我們在一個母字符串中查找一個子字符串有很多方法。KMP是一種最常見的改進算法,它可以在匹配過程中失配的情況下,有效地多往後面跳幾個字符,加快匹配速度。
當然我們可以看到這個算法針對的是子串有對稱屬性,如果有對稱屬性,那麽就需要向前查找是否有可以再次匹配的內容。
在KMP算法中有個數組,叫做前綴數組,也有的叫next數組,每一個子串有一個固定的next數組,它記錄著字符串匹配過程中失配情況下可以向前多跳幾個字符,當然它描述的也是子串的對稱程度,程度越高,值越大,當然之前可能出現再匹配的機會就更大。
這個next數組的求法是KMP算法的關鍵,但不是很好理解,我在這裏用通俗的話解釋一下,看到別的地方到處是數學公式推導,看得都蛋疼,這個篇文章僅貢獻給不喜歡看數學公式又想理解KMP算法的同學。
1、用一個例子來解釋,下面是一個子串的next數組的值,可以看到這個子串的對稱程度很高,所以next值都比較大。
位置i |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
前綴next[i] |
0 |
0 |
0 |
0 |
1 |
2 |
3 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
4 |
0 |
子串 |
a |
g |
c |
t |
a |
g |
c |
a |
g |
c |
t |
a |
g |
c |
t |
g |
申明一下:下面說的對稱不是中心對稱,而是中心字符塊對稱,比如不是abccba,而是abcabc這種對稱。
(1)逐個查找對稱串。
這個很簡單,我們只要循環遍歷這個子串,分別看前1個字符,前2個字符,3個... i個 最後到15個。
第1個a無對稱,所以對稱程度0
前兩個ag無對稱,所以也是0
依次類推前面0-4都一樣是0
前5個agcta,可以看到這個串有一個a相等,所以對稱程度為1前6個agctag,看得到ag和ag對成,對稱程度為2
這裏要註意了,想是這樣想,編程怎麽實現呢?
只要按照下面的規則:
a、當前面字符的前一個字符的對稱程度為0的時候,只要將當前字符與子串第一個字符進行比較。這個很好理解啊,前面都是0,說明都不對稱了,如果多加了一個字符,要對稱的話最多是當前的和第一個對稱。比如agcta這個裏面t的是0,那麽後面的a的對稱程度只需要看它是不是等於第一個字符a了。
b、按照這個推理,我們就可以總結一個規律,不僅前面是0呀,如果前面一個字符的next值是1,那麽我們就把當前字符與子串第二個字符進行比較,因為前面的是1,說明前面的字符已經和第一個相等了,如果這個又與第二個相等了,說明對稱程度就是2了。有兩個字符對稱了。比如上面agctag,倒數第二個a的next是1,說明它和第一個a對稱了,接著我們就把最後一個g與第二個g比較,又相等,自然對稱成都就累加了,就是2了。
c、按照上面的推理,如果一直相等,就一直累加,可以一直推啊,推到這裏應該一點難度都沒有吧,如果你覺得有難度說明我寫的太失敗了。
當然不可能會那麽順利讓我們一直對稱下去,如果遇到下一個不相等了,那麽說明不能繼承前面的對稱性了,這種情況只能說明沒有那麽多對稱了,但是不能說明一點對稱性都沒有,所以遇到這種情況就要重新來考慮,這個也是難點所在。
(2)回頭來找對稱性
這裏已經不能繼承前面了,但是還是找對稱成都嘛,最愚蠢的做法大不了寫一個子函數,查找這個字符串的最大對稱程度,怎麽寫方法很多吧,比如查找出所有的當前字符串,然後向前走,看是否一直相等,最後走到子串開頭,當然這個是最蠢的,我們一般看到的KMP都是優化過的,因為這個串是有規律的。
在這裏依然用上面表中一段來舉個例子:
位置i=0到14如下,我加的括號只是用來說明問題:
(a g c t a g c )( a g c t a g c) t
我們可以看到這段,最後這個t之前的對稱程度分別是:1,2,3,4,5,6,7,倒數第二個c往前看有7個字符對稱,所以對稱為7。但是到最後這個t就沒有繼承前面的對稱程度next值,所以這個t的對稱性就要重新來求。
這裏首要要申明幾個事實
1、t 如果要存在對稱性,那麽對稱程度肯定比前面這個c 的對稱程度小,所以要找個更小的對稱,這個不用解釋了吧,如果大那麽t就繼承前面的對稱性了。
2、要找更小的對稱,必然在對稱內部還存在子對稱,而且這個t必須緊接著在子對稱之後。
如下圖說明。
從上面的理論我們就能得到下面的前綴next數組的求解算法。
void SetPrefix(const char *Pattern, int prefix[])
{
int len=CharLen(Pattern);//模式字符串長度。
prefix[0]=0;
for(int i=1; i<len; i++)
{
int k=prefix[i-1];
//不斷遞歸判斷是否存在子對稱,k=0說明不再有子對稱,Pattern[i] != Pattern[k]說明雖然對稱,但是對稱後面的值和當前的字符值不相等,所以繼續遞推
while( Pattern[i] != Pattern[k] && k!=0 )
k=prefix[k-1]; //繼續遞歸
if( Pattern[i] == Pattern[k])//找到了這個子對稱,或者是直接繼承了前面的對稱性,這兩種都在前面的基礎上++
prefix[i]=k+1;
else
prefix[i]=0; //如果遍歷了所有子對稱都無效,說明這個新字符不具有對稱性,清0
}
}
通過這個說明,估計能夠理解KMP的next求法原理了,剩下的就很簡單了。我自己也有點暈了,實在不喜歡那些數學公式,所以用形象邏輯思維方法總結了一下。
////////
KMP還有一種寫法:這個寫法是經過N個人優化的:
1 int j = -1, i = 0; 2 next[0] = -1; 3 while(i < len) 4 { 5 if(j == -1 || ss[i] == ss[j]) 6 { 7 8 i++; 9 j++; 10 next[i] = j; 11 } 12 else 13 { 14 j = next[j]; 15 } 16 }
原文轉自:http://blog.csdn.net/yearn520/article/details/6729426
KMP算法的next[]數組通俗解釋