1. 程式人生 > >關於《數據結構》課本KMP算法的理解

關於《數據結構》課本KMP算法的理解

utl 數組 找到 數據 最大 課本 16px != outline

數據結構課上講的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算法的理解