資料結構與演算法 (八) KMP演算法
1.概念引導
模式匹配:假設P是給定的子串,T是待查詢的字串,要求從T中找到與P相同的所有子串,這個問題稱為模式匹配問題,P稱為模式,T稱為目標,如果T中存在一個或多個模式為P的子串,就給出該子串在T中的位置,稱為匹配成功,否則,稱為匹配失敗
2.演算法原理
1).樸素的模式匹配
用P中的字元依次與T中的字串進行比較,首先從T的最左端開始比較,如果對於所有的k=0,1,3,...m-1,均有t[k]=p[k],則匹配成功,否則,必有某個K(0≤k<m)使得t[k]=p[k],此時可以將P右移一個字元,重新進行比較
2)KMP 匹配演算法
使用KMP理由:從由於樸素的模式匹配演算法,可以看出,每當本次匹配不成功時,P右移動一個字元,下一趟的比較右總是從P的第0個字元開始,而不管上一趟比較的中間結果是什麼,因而回溯是不可避免的,而實際上這種回溯往往是不必要的,從而我們引出KMP匹配演算法。
KMP解決的問題:KMP匹配演算法是由Knuth Morris 和Pratt提出的一種快速模式匹配演算法:該演算法解決了兩個問題
①當比較出現不等時,確定下一趟比較前應該將P右移多少個字元
②P右移後應該從P中的那個字元開始和T中剛才比較時不等的那個字元繼續比較
KMP演算法的核心:KMP演算法藉助於next陣列,來確定當匹配過程中出現不等時,P右移的位數和開始比較的字元位置,在這個陣列中。next[i] 的值只與P的前 i+1 個字元本身有關,而與目標T無關,(也就是說移動的位數P的 i 個符有關),至此可以歸納出兩個公式:
條件:P[t]與T[t] 不相等
若 next[i]大於等於0,應將P右移i-next[i]個字元,用P中的第next[i]個字元與T[j]進行比較
若 next[i]等於-1,P中任何字元都不必再與T[i]比較,二應將P右移i+1個字元,P[0]和T[j+1]開始重新進行下一趟的比較
KMP 的關鍵是要計算next陣列,而next陣列具有一下性質:
性質1: next[i] 是一個整數 並且 滿足 -1≤next[i]<i
性質2:如果P[i]和T[i]不相等 P[0]=T[j-i],P[i]=T[j-i+1],...P[i-1]=T[j-1]
按照next[i]的含義,此時應將P右移i-next[i]個字元,用P[k](k=next[i]>0) 與T[j]繼續比較 。
為了保證這樣的比較時有效的,應有 P[0]=T[j-k],P[1]=T[j-k+1],...P[k-1]=T[j-1]
綜上所述 p[0]=p[i-k],p[1]=p[i-k+1]...p[k-1]=p[i-1]
亦即 next[i]的取值應使P字串的最左端k個字元組成的字串和最右端K個字元組成的字串相同
性質3:為了不丟失任何可能的成功匹配,當滿足性質2的K存在多個可能的取值時,k的取值應保證P右移的位數I-next[i]=i-k最小,亦即取滿足性質2的最大K
性質4:如果在P₁P₂....Pi-1的最左端和最右端不存在相同的子串(或者說僅存在相同的空子串),則K=0,亦即一旦在匹配過程中出現P[I]和T[j]比較時不相等,應將P右移i個字元,並從P[0]和T[j] 開始比較,顯然 當i=0且P[0]不等於T[j] 時需將P右移i-next[i]=1個字元並從P[0]和T[j]開始比較,這表明next[0]=-1
性質5:由性質2可知。在匹配過程中出現P[i]和T[j]比較時不相等P右移i-next[i]=i-k個字元,從P[k]和T[j]開始繼續比較而P中前K個字元無須再進行比較