標籤:字串 演算法
尾碼自動機(sam)上的字串匹配
====
我們把相對較短的模式串構造成sam。
對於P="abcabcacab", T[1..i]的尾碼,使得它是sam的最長前置長度:
T: b a b c b a b c a b c a a b c a b c a b c a c a b c
1 1 2 3 1 1 2 3 4 5 6 7 1 2 3 4 5 6 7 5 6 7 8 9 10 4
如果最長前置長度是|P|,則表示T[1..i]的尾碼和P匹配。
記憶體使用量
可能多個trans指標同一個節點,因此像刪除樹那樣會引起double-free:
為此我們暫時採用記憶體池的做法。
如果擴充到包括數字和空格,則需要表示37個轉移指標。
KMP演算法
====
給定模式串P,文本串T,
假設在s位置已匹配了q個字元, 即P[1,..,q]=T[s+1,..,s+q], 而在P[q+1]不匹配。
strstr()這時會把指標指向s+2,從P[1]重新開始匹配。
當時Knuth,Morris,Pratt就想可不可以把指標再移遠一點。
假設有P[1,..,k]=T[s+q+1-k,..,s+q],這時從P[k+1]開始比就行了,顯然我們希望k越大越好,對應地指標移動增量=q-k越小,因此應該不會錯過某些完全符合的位置。
我們把上面兩個等式合并,得到P[1,..,k]是P[1,..,q]的尾碼。問題變成:
對於每個q, 求P[1,..,q]的最長的真首碼(長度記為k),同時它也是P[1,..,q]的尾碼。
我們定義首碼函數pi(q):=k.
如何計算pi(q) ?
====
使用遞推的想法,假設我們已經計算好了pi(q)=k。
如果P[k+1] = P[q+1], 則顯然有pi(q+1) = k+1;
否則,看作是一個匹配問題, 我們來看pi(q)的含義是與P[1,..,q]末尾匹配的最長前置長度k,我們就拿這個首碼來匹配,並期望P[k+1]和P[q+1]一樣,否則k=pi(k)迴圈下去。
初始條件:pi(1) = 0, 因為最長真首碼是空串。
當前P[1,..,k]匹配T[q-k+1,q],而在T[q+1]不匹配,
應用首碼函數的定義,應該從位置s+1-k + q-k =
一個字串P[1,..,j]去匹配P[q+1-j,..q+1]的過程。
k=pi(k), 直到P[k] = P[q+1]。
如何做線性字串匹配?
====
參照尾碼自動機的做法, 我們把pi和P組成一個自動機,T在這個自動機上
走一遍。
首碼函數練習題目:
1. P 在T中的出現次數? 提示:檢查pi(PT)
2. (ab)^3 = ababab, 如何求最大的重複因子r=3?
3. 如何線上性時間內判斷是否為迴圈移位,比如arc和car。(這個我還不知道怎麼做)
KMP比SAM節省記憶體: