真正把kmp算法中的next數組含義和求法講明白


首先kmp算法是什么我就不啰嗦了。

直接講next數組。

next數組我是這樣定義的:該位置前面字符串的最長相同的真前綴和真后綴長度。

 

直接看這個字符串, ABABDABABAE:

 

中間一行是字符串的位置下標

首先,來看next數組第一個應該填什么。

很明顯,A這個位置前面壓根沒字符了,所以也不存在最長相同真前后綴。

按照定義,把next數組第一個填 0(實際一般填-1,這個是為什么后面再解釋)。

 

 

 

那么看第二個,字符是B,那么它前面是字符串A。

這里注意一下,求最長相同前后綴,真前綴是不包括自身的,真后綴同理。

那么也很顯然,A也沒有真前后綴,更別說相同的真前后綴了。

所以next數組第二個也填0。

 

 

 

 

看第三個,字符是A,那么它前面是字符串AB。

這次有真前后綴了,真前綴只有一個A,真后綴只有一個B。

相同的真前后綴長度還是0。

 

 

 

看第四個,字符是B,那么它前面是字符串ABA。

ABA的 真前綴有A, AB。真后綴有A, BA。

好,這次不是0了,A和A相同。

因此最長的相同的真前后綴長度是1。

所以next數組第4個填1。

 

 

 

 

后面不用多說。按照這思路求出來的next數組應該是這個樣子的:

 

 

這里我說個重要點:

next數組存的不是這個位置的 最長的相同的真前后綴長度,它存的是這個位置前面的字符串的 最長的相同的真前后綴長度。

所以看圖中,最后E的位置,這時候字符串是ABABDABABAE其實根本沒相同的真前后綴!

這個3指的是E前面的ABABDABABA最長的相同的真前后綴長度(即ABA)。

 

 

好,現在知道啥是next數組。但是這玩意怎么求呢?

我們是人,一眼看出相同前后綴。電腦不能看出來。

顯然要設計一種算法,來求一個字符串的next數組。

有人說暴力跑一遍不就知道了?

別忘了,我們發明KMP算法就是簡化字符串匹配的,你這里暴力匹配也談不上 簡化 二字了。

 

 

大家看數據結構書,看網上都會發現其實有一種簡便方法讓計算機快速求next數組。

這里先不貼代碼。

先看一種情況:

對於上面舉的例子,怎么求 next[8] 呢?

把原數組稱為array。

 

 

仔細想想就會發現,這個next數組其實是一個個按順序求值的。

雖然不知道 next[8](也就是字符B)對應的next數組值是多少,但是前面是已知的。

能不能根據前面已知的next數組值來推導?

注意到 next[7] = 2,這並不是指 array[7] 對應位置的字符串最長的相同的真前后綴長度。

前面已經說過這個 2 指的是 array[7] 對應位置前面字符的相同真前后綴,並不包括array[7]該位置本身。

實際上  arrary[7] 是A對 next[7] 有影響嗎? 根本沒有。把 arrary[7] 換成 K 這個位置 next[7] 也是2,這是由前面決定的。

顯然,這個字符 A 決定是后面 next[8] 的值,因為 next[8] 求得是 array[0]到array[7]這個字符串

這也就是說:要知道 next[8] 的值只需要看 next[7] 和 arrary[7] 即可!

 

 

 

 

 

 

 

OK,我們再來看這個 2 ,它是指該位置前面的字符串 最長的相同的真前后綴長度 為2。

也就是ABABDAB 這個字符串,它們前面兩個字符是AB,最后兩個字符也是AB。

接下來我們知道,數組都是由0開始計數的。

那么其實 array[ next[7] ] = array[2] = 原數組的第三個。

回到例子,

 

 

我們知道next[7]=2, 也就是array[7]前面兩個( array[5] 和 array[6] )和字符串開頭( array[0] 和 array[1] )兩個相同。

顯然,如果array[7] = array[2] = A,

那么 array[8]前面三個( array[5]   array[6]  array[7])和字符串開頭( array[0] 和 array[1]  array[2])三個相同。

 

 

 

得到結論一:

如果 array[ next[m] ] = = array[ m ],那么next[ m+1 ] = next[ m ] + 1;

比如說 next[m]=K ,意味着m位置前面字符串前K個和后K個相同(這里的后K個,也就是m位置前K個)

又由於數組從0計數,所以說 array[ next[m] ] = = array[ K ], 是指數組的第K+1個元素。也就是第一個不是前綴的位置!

現在m位置前字符串的前K個和后K個相同。而 array[  ] 數組的第K+1個元素 又等於 array[ m ]。

那么m+1個位置前字符串的前k+1個和后k+1個相同。

 

 

 

 

接下來討論 array[ next[m] ] != array[ m ] 的情況。

 還是這個例子,

next[10]應該是多少?

 

 

 

 

 

(array[ 9 ]=4,因為array[ 9 ] 前面字符串是ABABDABAB 相同部分是 ABAB 長度為4)

 

 

 

如圖, 

array[ next[9] ] = array[ 4 ] = D

而array[ 9 ] = A

array[ next[9] ] != array[ 9 ] ,

那么 next[ 10 ] 也不等於  next[ 9 ]+1了。

 

我們之所以能只比較兩個位置的值等不等就能確定最長前后綴,是因為前面 已經求出來相同的地方了。

 

 

我們只要看 array[ 4 ] 和array[ 9 ]不同,是因為前面4個肯定相同,因為next[9]=4;

 

OK,取長度為5的前后綴不同,那么短一點的前后綴有可能會相同。

我們看看取4個的情況:

 

 

 

假如4長度的前后綴相同的話,就意味着array[0~3]=array[6~9]

也就意味着array[0~2]=array[6~8]

 

 

如圖,只有綠圈里相同,我們才有必要比較 array[3] 是不是等於 array[9]。

問題是怎么知道綠圈里相不相同。

 

 

我們仔細想一下,藍圈是已知 next[9]=4 而 相同的前后綴。

所以前后兩個藍色圈內字符串是完全相同的。

如果綠圈也相同,那不是代表綠圈是藍色圈中字符串的相同前后綴?

而我們能不能知道藍圈內的相同前后綴是什么?

當然可以:

 

 

 next[4]位置存儲的就是前面4個的最長相同前后綴。

 

 

 我們看next[9]=4。就是粉色圈子里的部分,兩個字符串相同。

 而next[4]=2。就是藍色圈子里的部分,這四個部分都相同。

 那么現在看橙色部分,就只要比較array[9] = array[2], 因為arr[0~1]和arr[7~8]肯定相同了

比較發現確實array[9] = array[2],那么得出結論:arrar[10]=3;

 

分析一下, next[9]=4,就是前9個字符中前4個和后4個相同。

 那么,next[ next[9] ] =next [4]。

 也就是第一個不屬於前綴的位置,而next[4]代表是該位置前面字符串,就是之前的前綴。

 next [4] = 2,也就是之前的前綴的相同前后綴的長度。

 

這也就是為什么上面第一種假設肯定不行了,因為next[ next[9] ] =next [4] = 2。

取 三個前綴后綴 肯定不等,等的話 next [4] 就等於3了。

因此 那一種情況中 array[9] 和 array[3] 都沒有繼續比較的必要了。

 

 

 

 

如果一直不等呢?

將arrat[9]改為E:

 

 

 

明顯array[2]不等於array[9];

那么就按照之前思路繼續取:next[next[ next[9] ] ]   =  next[ next[ 4 ] ] = next [ 2 ] = 0。

比較 array[9] 和 array[0],還是不等。

但是next[0]=0,繼續嵌套不還是next [ 0 ]嗎?

所以將next[0]=-1;

next[next[ next[9] ] ]   =  next[ next[ 4 ] ] = next [ 0 ] = -1;

OK,檢測到-1就沒必要繼續嵌套了。

該位置不存在相同前后綴了。

 

 

 

 

 

再舉個例子

 如圖,將之前array[3] 和 arrar[8]改成E:

 

 

 

array[9] 等於 array[ next[9] ] 嗎?( array[ next[9] ] = array[4] )

(等於的話next[10] =  next[9] +1 =5)

不等於,比較 array[9]  和 array[ next[next[9] ] ] (array[ next[next[9] ] ] = array[ next[4] ] =array[0] )

array[9] = array[0] = A 那么,next[10] =  next[4] +1 =1

(不等於的話 next[next[next[9] ] ] = next[next[4] ]  = next[0] = -1;next[10] =0)

 

結論二:

設置arrry[0]=-1;

如果 array[ next[m] ] ! = array[ m ],

令t=m; t =next [t]

比較array[ next[t] ] 和 array[ m ],相同的話 next[ m ]=next[ t ]+1;

不同的話 t =next [t] 繼續比較。

當t=-1時,next[ m ]=0;

 

 

 

下面是隨意找的一串代碼:

 1 void Getnext(int next[],String t)
 2 {
 3    int j=0,k=-1;
 4    next[0]=-1;
 5    while(j<t.length-1)
 6    {
 7       if(k == -1 || t[j] == t[k])
 8      //等於-1就不繼續了,直接k++;  next[j] = k=0了;
 9      //不是-1還相同的話就直接k++;注意j先++了,所以是改變的下一個值。
10       {
11          j++;k++;
12          next[j] = k;
13       }
14       else k = next[k];
15    }
16 }

以上

寫的還是有點混亂,

圖書windows自帶畫圖軟件畫的,所以有些不整齊。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM