關於《數據結構》課本KMP算法的理解
數據結構課上講的KMP算法和我在ACM中學習的KMP算法是有區別的,這裏我對課本上的KMP算法給出我的一些想法。
原理和之前的KMP是一樣的https://www.cnblogs.com/wkfvawl/p/9768729.html,但是不同點在於之前的KPM中next數組存放的是到了該位時最大前後綴長度,而這裏的KMP中next數組存放的是j下一步需要移動的位置。
“利用已經部分匹配這個有效信息,保持i指針不回溯,通過修改 j 指針,讓模式串盡量地移動到有效的位置。”
所以,整個KMP的重點就在於當某一個字符與主串不匹配時,我們應該知道 j 指針要移動到哪?
接下來我們自己來發現j的移動規律:
如圖:C和B不匹配了,我們要把 j 移動到哪?顯然是第1位。為什麽?因為前面有一個A相同啊:
如下圖也是一樣的情況:
可以把 j 指針移動到第2位,因為前面有兩個字母是一樣的:
至此我們可以大概看出一點端倪,當匹配失敗時,j要移動的下一個位置 k。
存在著這樣的性質:最前面的k個字符和 j 之前的最後k個字符是一樣的。
如果用數學公式來表示是這樣的
P[0 ~ k-1] == P[j-k ~ j-1]
這個相當重要,如果覺得不好記的話,可以通過下圖來理解:
弄明白了這個就應該可能明白為什麽可以直接將 j 移動到 k 位置了。
因為:
當T[i] != P[j]時
有T[i-j ~ i-1] == P[0 ~ j-1]
由P[0 ~ k-1] == P[j-k ~ j-1]
必然:T[i-k ~ i-1] == P[0 ~ k-1]、
公式很無聊,能看明白就行了,不需要記住。
好,接下來就是重點了,怎麽求這個(這些)k呢?
因為在P的每一個位置都可能發生不匹配,也就是說我們要計算每一個位置 j 對應的k,所以用一個數組next來保存,
介紹next:
(1)、j退到某next值時字符比較相等,指示器變量值各加1後繼續比較;
(2)、next[j] = k,表示當 T[i] != P[j] 時,j 指針的下一個位置,也就是將要移動的位置
(3)、j退到-1的時候(即模式的第一個字符匹配失誤時),i和j都要分別加1,表明從主串的下一個字符起和模式串進行匹配
(-1 只是定義的初值)
好,先把這個放一邊,我們自己來推導思路,現在要始終記住一點,
next[j]的值(也就是k)表示,當P[j] != T[i]時,j指針的下一步移動位置。
先來看第一個:當j為0時,如果這時候不匹配,怎麽辦?
像上圖這種情況,j已經在最左邊了,不可能再移動了,這時候要應該是i指針後移。
如果是當j為1的時候呢?
顯然,j指針一定是後移到0位置的。因為它前面也就只有這一個位置了~~~
下面這個是最重要的,請看如下圖:
請仔細對比這兩個圖。
我們發現一個規律:
當P[k] == P[j]時,
有next[j+1] == next[j] + 1
其實這個是可以證明的:
因為在P[j]之前已經有P[0 ~ k-1] == p[j-k ~ j-1]。(next[j] == k)
這時候現有P[k] == P[j],我們是不是可以得到P[0 ~ k-1] + P[k] == p[j-k ~ j-1] + P[j]。
即:P[0 ~ k] == P[j-k ~ j],即next[j+1] == k + 1 == next[j] + 1。
這裏的公式不是很好懂,還是看圖會容易理解些。
那如果P[k] != P[j]呢?比如下圖所示:
像這種情況,如果你從代碼上看應該是這一句:k = next[k];為什麽是這樣子?你看下面應該就明白了。
現在你應該知道為什麽要k = next[k]了吧!像上邊的例子,我們已經不可能找到[ A,B,A,B ]這個最長的後綴串了,但我們還是可能找到[ A,B ]、[ B ]這樣的前綴串的。所以這個過程像不像在定位[ A,B,A,C ]這個串,當C和主串不一樣了(也就是k位置不一樣了),那當然是把指針移動到next[k]啦。
先看看next數據值的求解方法
位序 1 2 3 4 5 6 7 8 9
模式串 a b a a b c a b c
next值 0 1 1 2 2 3 1 2 3
next數組的求解方法是:
1.第一位的next值為0
2.第二位的next值為1
後面求解每一位的next值時,根據前一位進行比較
3.第三位的next值:第二位的模式串為b ,對應的next值為1;將第二位的模式串b與第一位的模式串a進行比較,不相等;則第三位的next值為1(其他情況均為1)
4.第四位的next值:第三位的模式串為a ,對應的next值為1;將第三位的模式串a與第一位的模式串a進行比較,相同,則第四位的next值得為1+1=2
5.第五位的next值:第四位的模式串為a,對應的next值為2;將第四位的模式串a與第二位的模式串b進行比較,不相等;第二位的b對應的next值為1,則將第四位的模式串a與第一位的模式串a進行比較,相同,則第五位的next的值為1+1=2
6.第六位的next值:第五位的模式串為b,對應的next值為2;將第五位的模式串b與第二位的模式中b進行比較,相同,則第六位的next值為2+1=3
7.第七位的next值:第六位的模式串為c,對應的next值為3;將第六位的模式串c與第三位的模式串a進行比較,不相等;第三位的a對應的next值為1,
則將第六位的模式串c與第一位的模式串a進行比較,不相同,則第七位的next值為1(其他情況)
8.第八位的next值:第七位的模式串為a,對應的next值為1;將第七位的模式串a與第一位的模式串a進行比較,相同,則第八位的next值為1+1=2
9.第八位的next值:第八位的模式串為b,對應的next值為2;將第八位的模式串b與第二位的模式串b進行比較,相同,則第九位的next值為2+1=3
如果位數更多,依次類推
1 void getNext(char *p,int *next) 2 { 3 int j,k; 4 next[1]=0; 5 j=1; 6 k=0; 7 while(j<strlen(p)-1) 8 { 9 if(k==0||p[j]==p[k]) //匹配的情況下,p[j]==p[k],next[j+1]=k+1; 10 { 11 j++; 12 k++; 13 next[j]=k; 14 } 15 else //p[j]!=p[k],k=next[k] 16 k=next[k]; 17 } 18 }
關於《數據結構》課本KMP算法的理解