最近剛好學到了kmp算法,對我來說還蠻難的,原理還好理解,就是next數組的求解讓我很懵
旁聽了一下隔壁班大佬的分享,覺得他們講得特別好,就想來記錄一下
最長公共前后綴
kmp算法首先要找“最長公共前后綴”,其定義為:A的“最長公共前后綴”是“A中以最后一個字符結尾的非前綴子串”與“A的前綴”能夠匹配的最大長度。
例:ababab
我們從“最大長度”6開始向下枚舉
若答案是6。根據定義,“A中以最后一個字符結尾的非前綴子串”是ababab?我們注意到,ababab也算它自身的一個前綴
因此,在枚舉時,要從這個字符串的長度減一開始枚舉!
若答案是5,則“非前綴子串”為babab。而“前綴”是ababa。兩者不相等。
若答案是4,則“非前綴子串”為abab,而“前綴”是abab。兩者相等。
所以我們可以確定,字符串”ababab”的“最長公共前后綴”長度為4。
有什么用?
舉例說明:有兩個字符串
S: abaacababcac
T: ababc
首先,根據最長公共前后綴,我們寫出T串的所有前綴的“最長公共前后綴”長度:
T[0~0] = “a”,最長公共前后綴長度:0
T[0~1] = “ab”,最長公共前后綴長度:0
T[0~2] = “aba”,最長公共前后綴長度:1
T[0~3] = “abab”,最長公共前后綴長度:2
T[0~4] = “ababc”,最長公共前后綴長度:0
我們把求得的這5個數記為X數組(因為它還不是next數組)
這個更改后的X數組就是next數組,下圖是上面的執行
next數組的含義是:當S[i]和T[j]不相等,並且j不等於-1時,j指針該指向的位置,且i指針不動。
當然我們目前只是枚舉,接下來如何用算法實現求next數組
求next數組
假設一個新的字符串
R: a b c a b d e a b c a b c f
next: -1 0 0 0 1 ?
我們要求next[5]
情況一 R[i-1] == R[next[i-1]]
比如R[5-1]=R[1],所以顯然next[6]=next[5]+1=2
情況二 若R[i-1]!=R[next[i-1]]
R: a b c a b d e a b c a b c f
next: -1 0 0 0 1 2 ?
我們發現R[i-1]和R[next[i-1]]並不相等。
用肉眼可以看出,我們在abcabd不管怎么取后綴,后綴的最后一個字符d,從來就沒有在前面出現過。所以它不可能和任何一個前綴相等。所以next[6]才是0。
假設我們求到了這種程度
R: a b c a b d e a b c a b c f
next: -1 0 0 0 1 2 0 0 1 2 3 4 5 ?
發現R[2] == ‘c’。換言之,枚舉next[13]時,我們發現6不行,5不行,4不行,其原因都在最后一個字符’c’不是前綴的最后一個字母。
剛才我們發現R[next[12]],也就是R[5],和這個最后一個字符是不一樣的
但是,R[next[5]] = R[2] = ‘c’就和R[12]是一樣的。也就是說,R[12] == R[next[next[12]]]。
這不是巧合。可以證明,只要找到了這個’c’,且next[next[12]]=2,則R[13]就是2+1=3。並且可以證明,除了next[12]+1,next[next[12]]+1,next[next[next[12]]]+1…以外,next[13]不會有其他的答案了。
只要多次迭代,就能求出next[13]。
經過多次迭代,如果發現迭代到了-1這里還沒有找到最后一個字符,那么我們直接讓next[13]=-1+1=0就可以了。這也是我們讓那個X[0]=-1的又一個原因。
總結
這就是求整個next數組的詳細過程了,匹配的過程大家應該都懂,就不說了。