首先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自帶畫圖軟件畫的,所以有些不整齊。