1 問題的提出
給出字符串P和T,長度分別爲n和m。找出P在T中出現的所有位置。
2 原始匹配算法
int Index(char* P,char* T,int pos)
{
i = pos; j = 0;
while( i<=strlen(T) && j<=strlen(P) ){
if(P[j] == T[i]) {++i; ++j;}
else {i -= j; j = 0;}
}
if(j> strlen(P)) return (i – strlen(P) + 1);
else return -1;
}
上述算法的最壞時間複雜度爲O(mn)。boyer-moore算法、KMP算法、suffix tree算法(後綴樹)能夠在線性時間內處理的字符串的匹配問題。suffix tree算法需要對字符串T進行預處理,而boyer-moore算法和KMP算法需要對P進行預處理。所以,suffix tree比較適合T不變,P變化的場景。boyer-moore算法和KMP算法適合T變化,P不變的場景。本文即是對boyer-moore算法的理論性介紹。
3 字符串匹配算法之boyer-moore算法
3.1 從右到左掃描
在進行一次的匹配過程中,掃描P是從右向左的。如下圖,在第一次的匹配過程中,首先比較P[7]和T[7],然後比較P[6]和T[6],從右向左滑動。3.2 壞字符規則
見圖2。第一次比較:T[7](=x) ≠ P[7](=a),結果是不匹配,此時如果我們知道x在P中出現的right-most位置爲4,就可以移動P至圖3中所示的位置。定義:
字符集中的任一字符x,R(x)表示在P中出現的right-most位置,如果x沒有在P中出現,則R(x)置爲0。計算R(x)的時間複雜度爲O(n)。
壞字符規則:
在某次匹配過程中,P的最右邊的 n-i 個字符已經和T中的匹配,但是第i個字符不匹配(對於T中的第k個字符),此時P應該向右移動 max( 1, i – R(T[k]) )。
壞字符規則在實際應用中特別高效。但是如果字符集比較小,則效率較差,時間複雜度達不到線型。
3.3 好後綴規則
好後綴規則:在某次的匹配過程中,P的一個後綴匹配了T中的的子串t,但是子串t左邊的第一個字符不匹配,如果P中存在滿足如下條件的子串t`:
1 t`=t,但t`不是P的後綴
2 在P中位於t`左邊的第一個字符 ≠ 在P中位於t左邊的第一個字符
3 在滿足第1,2條件下,在P中位置處於最右的t`
則移動P使得t` 與T中的t對齊,如圖5所示。如果不存在,則向右移動P最少距離,使得P的某個前綴匹配T中的t的某個後綴,如圖6。如果還沒有匹配的,則P向右移動n個位置,如圖7。
【定義】
對於任一i∈(1,n),L(i) 是滿足下列條件的值:
1 L(i) < n
2 字符串P[i…n] 匹配P[1…L(i)]的後綴
3 如果存在滿足條件1和2的情況,則取最大值 ,否則取值爲0
【定義】
Nj(P)是 滿足如下字符串的長度:
1 是P[1…j]的後綴
2 是P的後綴
3 滿足條件1,2的最長字符串
【定理】
L(i) 是 滿足條件Nj(P) = |P[i…n]|= n–i +1 的j的最大值。
根據如上定理,計算L(i)的算法如下:
for i:=1 to n do L(i) := 0;
for j:=1 to n-1 do
begin
i:=n - Nj(P) + 1;
L(i) = j;
end;
3.4 完整的boyer-moore算法
在3.2和3.3節中,給出了2個移動的位置。在boyer-moore算法中,取2個值中的最大值。3.5 複雜度分析
在壞字符規則中,計算R(x)的時間複雜度爲O(n);在好後綴規則中,計算L(i)的時間複雜度爲O(n)。所以boyer-moore算法的時間複雜度爲O(m+n)。4 KMP算法
假設主串爲s1 s2 …sn 模式串爲p1 p2…pm 。當匹配過程中產生失配(即si≠pj)時,模式串向右滑行多遠,換句話說,當主串第i個字符與模式串中的第j個字符發生失配時,主串中的第i個字符(i指針不回溯)應用與模式中的哪個字符比較?next[j] = 0 當j=1
= Max{k| 1<k<j 且 p1…pk = pj-j+1…pj-1 }
=1 其它情況
複雜度分析。計算next數組的複雜度爲O(m),所以最終匹配的複雜度爲O(m+n)。