理解我接下來所說的東西,需要大家懂得簡單的動態規划。
KMP大家都不陌生了,但是其中計算next數組總是搞不明白,我想有很多人和我一樣。所以這里用動態規划的思路去描述一下這個問題。
模式串P=c[1]c[2]......c[n]
先設幾個符號:
suffix(S): S的所有后綴的集合
prefix(S): S的所有前綴的集合
例如:suffix("abcd") = {"","d", "cd", "bcd"}
prefix("abcd") = {"","a", "ab", "abc"}
看到,在這兩個集合都除去了"abcd"本身。
真對P定義next的意義:next[i]代表的意義是suffix(c[1]c[2]......c[i])與prefix(c[1]c[2]......c[i])兩個集合中最長相同串的長度(不包括串c[1]c[2]......c[i])。那么如何計算next呢?其實這個計算過程類似動態規划的轉移過程,從初始狀態,不斷的根據轉移方程計算,直至計算出所有的可達狀態。
那么狀態怎么定義?初始條件是什么?
設兩個游標p1,p2, p1,p2構成的位置對為狀態<p1,p2>,例如p1在i位置,p2在j位置,i<j,那么狀態<i, j>就代表c[1]c[2]......c[i]=c[j-i+1]c[j-i+2]......c[j],即串的前i個字符與后i個字符相同。之后我們根據<i,j>推出<i',j+1>。我們依次計算<i1, 1>,<i2,2>,......,<in,n>。設LMAX(<i, j>)=i,通過上面對next的定義,我們可知next[j]=MAX{LMAX(<i,j>)|0<=i<j}。
初始化的條件為 next[1] = LMAX(<0, 1>) = 0。假設我們當前所在的狀態為<i, j>,那么如何推出<i',j+1>呢?這必須得看c[i+1]與c[j+1]的關系,如果c[i+1]=c[j+1],那么我們到達<i+1,j+1>的狀態;如果c[i+1]!=c[j+1],那么我們得根據所有可能的<i', j>狀態,判斷c[i'+1]=c[j+1]是否成立,如果成立就到達<i'+1,j+1>狀態。那么,我們如何找到這些合法的<i',j>狀態呢?很慶幸,因為next數組中天然的保存了我們需要的信息。當我們到達<i,j>這個狀態並且發現c[i+1]!=c[j+1],那么下一個嘗試的狀態將是<next[i], j>,看看c[next[i]+1]=c[j+1]是否成立。而且我們發現,只要按照i,next[i],next[next[i]]......這樣下去找到第一個能夠符合轉移條件的狀態就OK了,如果沒有一個能夠使之轉移的狀態,就說明沒有一個前綴和當前的某個后綴是相等的,那么直接跳轉到<0, j+1>這個狀態。
舉個例子吧: S=abcababc 為了方便理解,在S前加一個通配符$ $abcababc <0, 1> next[1]=0 || $abcababc <0, 2> next[2]=0 | | $abcababc <0, 3> next[3]=0 | | $abcababc <1, 4> next[4]=1 | | $abcababc <2, 5> next[5]=2 | | 下一步s[3]!=s[6],這將嘗試狀態<next[3],j>=<0,j>,使之轉移到下面的狀態 $abcababc <1, 6> next[6]=1 | | $abcababc <2, 7> next[7]=2 | | $abcababc <3, 8> next[8]=3 | |
到此,有的同學又有疑問了,人家KMP定義的next[i]的意義和你的也不一樣呀。對,確實不一樣。
KMP中對next[i]的定義為:設文本串為T,模式串為P,p[i]!=T[j]時,應該用p[?]與T[j]進行比較。
對應於上例,得到的next數組值應為
KMP的next: -1 1 1 1 2 3 2 3
我們的next: 0 0 0 1 2 1 2 3
怎么通過我們的next獲取到正確的next呢?非常簡單,從后往前循環做next[i] = next[i-1]+1,之后next[1]=-1就哦了。