符號說明i
主串當前位置的下標(不回溯)
j模式串t當前位置的下標
目的:原本的匹配時 i 和 j 不停的回溯,kmp模式匹配讓 i 不用回溯。
j 值的變化與主串無關,取決於模式串t的結構中是否有重複(即當前字元之前的串的前字尾的相似度)。
我們把模式串t各個位置的 j 值的變化定義為乙個陣列 next,那麼next的長度就是模式串t的長度。由此得出以下的函式定義:
n ex
t[j]
=,
當此集合不為空時1,
其他情況
next[j]= \begin 0,& \text\\ max\,1
,當j=1
時當此集合不為空時
其他情況
具體要怎麼推導出來乙個串的next陣列值呢,來看一些例子:
1、t=「abcdex」
j123456
模式串t
abcdex
next[j]
011111
當 j=1 時,next[1]=0;
當 j=2 時,j 由1到 j-1 就只有字串 「a」,屬於其他情況,next[2]=1;
當 j=3 時,j 由1到 j-1 就只有字串 「ab」,顯然「a」與「b」不相等,屬於其他情況,next[3]=1;
以後同理可得,得出最終t串的next[j] 為011111
2、t=「abcabx」
j123456
模式串t
abcabx
next[j]
011123
當 j=1 時,next[1]=0;
當 j=2 時,同上例說明,next[2]=1;
當 j=3 時,同上next[3]=1;
當 j=4 時,同上next[4]=1;
當 j=5 時,此時j 由1到 j-1 字串是 「abca」,字首字元的「a」(黃色強調)與字尾字元的「a」相等,因此可推算出k值為2(由』p[1]…p[k-1]』=『p[j-k+1]…p[j-1]』,得到p[1]=p[4])因此next[5]=2;
當 j=6 時,j 由1到 j-1 字串是 「abcab」,字首字元的「ab」與字尾字元的「ab」相等,因此可推算出k值為3(由』p[1]…p[k-1]』=『p[j-k+1]…p[j-1]』,得到p[1]p[2]=p[4]p[5])因此next[6]=3;
我們可以根據經驗得到如果前字尾乙個字元相等,k的值是2,兩個字元k的值是3。n個相等的k值就是n+1。
3、t=「ababaaaba」
j123456789
模式串t
ababaaaba
next[j]
011234223
當 j=1 時,next[1]=0;
當 j=2 時,同上next[2]=1;
當 j=3 時,同上next[3]=1;
當 j=4 時,j 由1到 j-1 的字串是 「aba」,字首字元的「a」(黃色強調)與字尾字元的「a」相等,next[4]=2;
當 j=5 時,j 由1到 j-1 字串是 「abab」,字首字元的「ab」(黃色強調)與字尾字元的「ab」相等,因此next[5]=3;
當 j=6 時,j 由1到 j-1 字串是 「ababa」,字首字元的「aba」與後面的「aba」相等,因此next[6]=4;
當 j=7 時,j 由1到 j-1 字串是 「ababaa」,由於字首字元的「ab」與後面的「aa」不相等,只有「a」相等,因此next[7]=2;
當 j=8 時,j 由1到 j-1 字串是 「ababaaa」,只有「a」相等,因此next[8]=2;
當 j=9 時,j 由1到 j-1 字串是 「ababaaab」,字首字元的「ab」(黃色強調)與字尾字元的「ab」相等,因此next[9]=3;
4、 t=「aaaaaaaab」
j123456789
模式串t
aaaaaaaab
next[j]
012345678
當 j=1 時,next[1]=0;
當 j=2 時,同上next[2]=1;
當 j=3 時,j 由1到 j-1 的字串是 「aa」,字首字元的「a」(黃色強調)與字尾字元的「a」相等,next[3]=2;
當 j=4 時,j 由1到 j-1 的字串是 「aaa」,字首字元的「aa」(黃色強調)與字尾字元的「aa」相等,next[4]=3;
…當 j=9 時,j 由1到 j-1 字串是 「aaaaaaaa」,字首字元的「aaaaaaa」與後面的「aaaaaaa」相等,因此next[6]=8;
/*通過計算返回子串t的next陣列*/
void
get_next
(string t, vector<
int>
& next)
//next長度要比t的長度大1,next從0開始,t從1開始
else
i = next[i]
;/*若字元不相同,則i回溯到前面重新比較*/}}
/*返回匹配串t在主串s中第pos個字元之後的位置。若不存在,則函式返回值為0*/
/*t非空,1<=pos<=s.length()*/
intindex_kmp
(string s, string t,
int pos)
else
/*回溯*/}if
(j >= t_len)
return i - t_len;
else
return-1
;}
前者依然存在缺陷:當主串s=「aaaabcde」,匹配串t=「aaaaax」,其next陣列值分別012345,在開始時,當 i=5、j=5 時,我們發現 「b」 和 「a」 不相等,因此j=next[5]=4,此時 「b」 與第四位置的 「a」 依然不等, j=next[4]=3,後面 j 依次是 3 2 ,直到 j=next[1]=0 時,根據演算法,i++、j++,得到 i=6、j=1。
我們發現,j從等於4直到等於0這些步驟都是多餘的判斷。由於t串的第2、3、4、5位置的字元與首位的 「a」 相等,那麼可以用首位next[1]的值去替代與他相等的後續字元 next[j] 的值,因此我們對next函式進行改良。
void
get_nextval
(string t, vector<
int>
& nextval)
else
i = nextval[i];}
}
1、t=「ababaaaba」
j123456789
模式串t
ababaaaba
next[j]
011234223
nextval[j]
010104210
當 j=1 時,next[1]=0;
當 j=2 時,因為第二位字元 「b」 的next值為1,而第一位就是 「a」 ,它們不相等,所以nextval[2]=next[2]=1,維持原值;
當 j=3 時,因為第三位字元 「a」 的next值為1,所以與第一位的 「a」 比較得知它們相等,,所以nextval[3]=nextval[1]=0;
當 j=4 時,第四位字元 「b」 的next值為2,所以與第二位的 「b」 比較得知它們相等,,所以nextval[4]=nextval[2]=0;
當 j=5 時,next值為3,第五個字元 「a」 與第三個字元 "a"相等,所以nextval[5]=nextval[3]=0;
當 j=6 時,next值為4,第六個字元 「a」 與第四個字元 "b"不相等,所以nextval[6]=4;
當 j=7 時,next值為4,第六個字元 「a」 與第四個字元 "b"不相等,所以nextval[6]=4;
當 j=8 時,next值為2,第八個字元 「b」 與第二個字元 "b"相等,所以nextval[8]=nextval[2]=1;
當 j=9 時,next值為3,第九個字元 「a」 與第三個字元 "a"相等,所以nextval[9]=nextval[3]=1;
4、 t=「aaaaaaaab」
j123456789
模式串t
aaaaaaaab
next[j]
012345678
nextval[j]
000000008
當 j=1 時,nextval[1]=0;
當 j=2 時,next值為1,第二個字元與第乙個字元相等,所以nextval[2]=nextval[1]=0;
同樣的道理,後面都為0…
當 j=9 時,next值為8,第九個字元 「b」 與第八個字元 「a」 不相等,所以nextval[9]=8;
總結:改進的kmp演算法,它是在計算出next值的同時,如果 a 位字元與它的 next值指向的b位字元相等,則該 a 位的nextval就指向nextval值,如果不等,則該 a 位的nextval值就是它自己 a 位的next值。
模式匹配 KMP演算法
字串匹配演算法 include includeusing namespace std define ok 1 define error 0 define overflow 2 typedef int status define maxstrlen 255 使用者可在255以內定義最長串長 typed...
模式匹配KMP演算法
前些日子在為目前該學習什麼而苦惱,就問了一下已經從事多年軟體開發的表哥,他說乙個程式設計師要走的遠,就要學好資料結構和演算法,於是我就重新開始學習資料結構和演算法了 拿起以前上過的資料結構看,看到第四章串的模式匹配時,頗感興趣,就寫了一下程式,實踐了一下。感覺還蠻爽,於是就把以下幾個重要的函式放在此...
KMP模式匹配演算法
首先,這是由knuth morris和prattle三人設計的線性時間字串匹配演算法。這裡就不仔細的講解,網上有更好的講解,在這推薦幾個自己當時學習的kmp所看到的較好理解的 這裡附上自己所學的 includeusing namespace std s 是主串 p 是模式串 int next 100...