在用於查找子字符串的算法當中,BM(Boyer-Moore)算法是目前相當有效又容易理解的一種,一般情況下,比KMP算法快3-5倍。
BM算法在移動模式串的時候是從左到右,而進行比較的時候是從右到左的。
BM算法實際上包含兩個並行的算法,壞字符算法和好后綴算法。這兩種算法的目的就是讓模式串每次向右移動盡可能大的距離(j+=x,x盡可能的大)。
幾個定義:
例主串和模式串如下:
主串 : mahtavaatalomaisema omalomailuun
模式串: maisemaomaloma
好后綴:模式串中的aloma為“好后綴”。
壞字符:主串中的“t”為壞字符。
好后綴算法
如果程序匹配了一個好后綴, 並且在模式中還有另外一個相同的后綴, 那
把下一個后綴移動到當前后綴位置。好后綴算法有兩種情況:
Case1:模式串中有子串和好后綴安全匹配,則將最靠右的那個子串移動到好后綴的位置。繼續進行匹配。
Case2:如果不存在和好后綴完全匹配的子串,則在好后綴中找到具有如下特征的最長子串,使得P[m-s…m]=P[0…s]。說不清楚的看圖。
壞字符算法
當出現一個壞字符時, BM算法向右移動模式串, 讓模式串中最靠右的對應字符與壞字符相對,然后繼續匹配。壞字符算法也有兩種情況。
Case2:模式串中不存在壞字符。見圖。
移動規則
BM算法的移動規則是:
將概述中的++j,換成j+=MAX(shift(好后綴),shift(壞字符)),即
BM算法是每次向右移動模式串的距離是,按照好后綴算法和壞字符算法計算得到的最大值。
shift(好后綴)和shift(壞字符)通過模式串的預處理數組的簡單計算得到。好后綴算法的預處理數組是bmGs[],壞字符算法的預處理數組是BmBc[]。
BM算法子串比較失配時,按壞字符算法計算模式串需要向右移動的距離,要借助BmBc數組。
注意BmBc數組的下標是字符,而不是數字。
BmBc數組的定義,分兩種情況。
1、 字符在模式串中有出現。如下圖,BmBc[‘k’]表示字符k在模式串中最后一次出現的位置,距離模式串串尾的長度。
2、 字符在模式串中沒有出現:,如模式串中沒有字符p,則BmBc[‘p’] = strlen(模式串)。
BM算法子串比較失配時,按好后綴算法計算模式串需要向右移動的距離,要借助BmGs數組。
BmGs數組的下標是數字,表示字符在模式串中位置。
BmGs數組的定義,分三種情況。
1、 對應好后綴算法case1:如下圖:i是好后綴之前的那個位置。
2、 對應好后綴算法case2:如下圖所示:
3、 當都不匹配時,BmGs[i] = strlen(模式串)
在計算BmGc數組時,為提高效率,先計算輔助數組Suff。
Suff數組的定義:suff[i] = 以i為邊界, 與模式串后綴匹配的最大長度,即P[i-s...i]=P[m-s…m]如下圖:
舉例如下:
分析
用Suff[]計算BmGs的方法。
1) BmGs[0…m-1] = m;(第三種情況)
2) 計算第二種情況下的BmGs[]值:
for(i=0;i
if(-1==i || Suff[i] == i+1)
for(;j < m-1-i;++j)
if(suff[j] == m)
BmGs[j] = m-1-i;
3) 計算第三種情況下BmGs[]值,可以覆蓋前兩種情況下的BmGs[]值:
for(i=0;i
BmGs[m-1-suff[i]] = m-1-i;
如下圖所示:
Suff[]數組的計算方法。
常規的方法:如下,很裸很暴力。
Suff[m-1]=m;
for(i=m-2;i>=0;--i){
q=i;
while(q>=0&&P[q]==P[m-1-i+q])
--q;
Suff[i]=i-q;
}
有聰明人想出一種方法,對常規方法進行改進。基本的掃描都是從右向左。改進的地方就是利用了已經計算得到的suff[]值,計算現在正在計算的suff[]值。
如下圖所示:
i是當前正准備計算的suff[]值得那個位置。
f是上一個成功進行匹配的起始位置(不是每個位置都能進行成功匹配的, 實際上能夠進行成功匹配的位置並不多)。
q是上一次進行成功匹配的失配位置。
如果i在q和f之間,那么一定有P[i]=P[m-1-f+i];並且如果suff[m-1-f+i]=i-q, suff[i]和suff[m-1-f+i]就沒有直接關系了。
1 int BMMatch(byte* pSrc, int nSrcSize, byte* pSubSrc, int nSubSrcSize) 2 { 3
4 //1.壞字符數組
5 int bcSkip[256]; 6 for( int i = 0; i < 256; i++) 7 { 8 bcSkip[i] = nSubSrcSize; 9 } 10 for (int i = 0; i < nSubSrcSize - 1; i++) 11 { 12 bcSkip[pSubSrc[i]] = nSubSrcSize - i - 1; 13 } 14
15 //2.好后綴數組
16 int* suffix = new int [nSubSrcSize]; 17 suffix[nSubSrcSize - 1] = nSubSrcSize; 18 for (int i = nSubSrcSize - 2; i >= 0; i--) 19 { 20
21 int k = i; 22 while( k >= 0 && pSubSrc[k] == pSubSrc[nSubSrcSize-1-i+k] ) 23 { 24 k--; 25 } 26 suffix[i] = i - k; 27 } 28
29 int* gsSkip = new int [nSubSrcSize]; 30 for (int i = 0; i < nSubSrcSize; i++) 31 { 32 gsSkip[i] = nSubSrcSize; 33 } 34 for (int i = nSubSrcSize - 1; i >= 0; i--) 35 { 36 if (suffix[i] == i + 1) 37 { 38 for (int j = 0; j < nSubSrcSize - 1 - i; ++j) 39 { 40 if (gsSkip[j] == nSubSrcSize) 41 gsSkip[j] = nSubSrcSize - 1 - i; 42 } 43 } 44 } 45 for (int i = 0; i <= nSubSrcSize - 2; ++i) 46 { 47 gsSkip[nSubSrcSize - 1 - suffix[i]] = nSubSrcSize - 1 - i; 48 } 49
50 int nPos = 0; 51 while (nPos <= nSrcSize - nSubSrcSize) 52 { 53 int j = nSubSrcSize - 1; 54 while(j >= 0 && pSubSrc[j] == pSrc[j + nPos]) 55 { 56 j--; 57 } 58 if (j < 0) 59 break; 60 else
61 { 62 nPos += max(gsSkip[j], bcSkip[pSrc[j + nPos]]-(nSubSrcSize - 1 - j) ); 63 } 64 } 65 delete[] gsSkip; 66 return (nPos > nSrcSize - nSubSrcSize)? -1 : nPos; 67 }
1 int BMMatchEx(byte* pSrc, int nSrcSize, byte* pSubSrc, int nSubSrcSize) 2 { 3
4 //1.壞字符數組
5 int bcSkip[256]; 6 for( int i = 0; i < 256; i++) 7 { 8 bcSkip[i] = nSubSrcSize; 9 } 10 for (int i = 0; i < nSubSrcSize - 1; i++) 11 { 12 bcSkip[pSubSrc[i]] = nSubSrcSize - i - 1; 13 } 14
15 //2.好后綴數組
16 int* suffix = new int [nSubSrcSize]; 17 suffix[nSubSrcSize - 1] = nSubSrcSize; 18 int g = nSubSrcSize - 1; 19 int f = 0; 20 for (int i = nSubSrcSize - 2; i >= 0; i--) 21 { 22 if(i > g && suffix[i + nSubSrcSize - 1 - f] < i - g) 23 { 24 suffix[i] = suffix[i + nSubSrcSize - 1 - f]; 25 } 26 else
27 { 28 if (i < g) 29 { 30 g = i; 31 } 32 f = i; 33 while( g >= 0 && pSubSrc[g] == pSubSrc[nSubSrcSize-1-f+g] ) 34 { 35 g--; 36 } 37 suffix[i] = f - g; 38 } 39 } 40
41 int* gsSkip = new int [nSubSrcSize]; 42 for (int i = 0; i < nSubSrcSize; i++) 43 { 44 gsSkip[i] = nSubSrcSize; 45 } 46 for (int i = nSubSrcSize - 1; i >= 0; i--) 47 { 48 if (suffix[i] == i + 1) 49 { 50 for (int j = 0; j < nSubSrcSize - 1 - i; ++j) 51 { 52 if (gsSkip[j] == nSubSrcSize) 53 gsSkip[j] = nSubSrcSize - 1 - i; 54 } 55 } 56 } 57 for (int i = 0; i <= nSubSrcSize - 2; ++i) 58 { 59 gsSkip[nSubSrcSize - 1 - suffix[i]] = nSubSrcSize - 1 - i; 60 } 61
62 int nPos = 0; 63 while (nPos <= nSrcSize - nSubSrcSize) 64 { 65 int j = nSubSrcSize - 1; 66 while(j >= 0 && pSubSrc[j] == pSrc[j + nPos]) 67 { 68 j--; 69 } 70 if (j < 0) 71 break; 72 else
73 { 74 nPos += max(gsSkip[j], bcSkip[pSrc[j + nPos]]-(nSubSrcSize - 1 - j) ); 75 } 76 } 77 delete[] gsSkip; 78 return (nPos > nSrcSize - nSubSrcSize)? -1 : nPos; 79 }