Boyer-Moore算法學習(轉)

1、概述
在用於查找子字符串的算法當中,BM(Boyer-Moore)算法是目前相當有效又容易理解的一種,一般情況下,比KMP算法快3-5倍。

BM算法在移動模式串的時候是從左到右,而進行比較的時候是從右到左的。

常規的匹配算法移動模式串的時候是從左到右,而進行比較的時候也是是從左到右的,基本框架是:

+ expand sourceview plaincopy to clipboardprint?
j = 0;  
 
while(j <= strlen(主串)- strlen(模式串)){  
    for (i = 0;i < strlen(模式串) && 模式串[i] == 主串[i + j]; ++i)  
        ;   
    if (i == strlen(模式串))  
        Match;  
    else   
        ++j;  

j = 0;

while(j <= strlen(主串)- strlen(模式串)){
    for (i = 0;i < strlen(模式串) && 模式串[i] == 主串[i + j]; ++i)
        ;
    if (i == strlen(模式串))
        Match;
    else
        ++j;
}

 

而BM算法在移動模式串的時候是從左到右,而進行比較的時候是從右到左的,基本框架是:

+ expand sourceview plaincopy to clipboardprint?
j = 0;   
while (j <= strlen(主串) - strlen(模式串)) {   
   for (i = strlen(模式串) - 1; i >= 0 && 模式串[i] ==主串[i + j]; --i)   
      if (i < 0)  
          match;  
       else   
          ++j;  

j = 0;
while (j <= strlen(主串) - strlen(模式串)) {
   for (i = strlen(模式串) - 1; i >= 0 && 模式串[i] ==主串[i + j]; --i)
      if (i < 0)
          match;
       else
          ++j;
}
 

顯然BM算法並不是上面那個樣子,BM算法的精華就在於++j

2、BM算法思想
BM算法實際上包含兩個並行的算法,壞字符算法和好後綴算法。這兩種算法的目的就是讓模式串每次向右移動儘可能大的距離(j+=x,x儘可能的大)。

幾個定義:

例主串和模式串如下:

主串  :  mahtavaatalomaisema omalomailuun

模式串: maisemaomaloma

好後綴:模式串中的aloma爲“好後綴”。

壞字符:主串中的“t”爲壞字符。

好後綴算法
如果程序匹配了一個好後綴, 並且在模式中還有另外一個相同的後綴, 那

把下一個後綴移動到當前後綴位置。好後綴算法有兩種情況:

Case1:模式串中有子串和好後綴安全匹配,則將最靠右的那個子串移動到好後綴的位置。繼續進行匹配。

 

Case2:如果不存在和好後綴完全匹配的子串,則在好後綴中找到具有如下特徵的最長子串,使得P[m-s…m]=P[0…s]。說不清楚的看圖。

 

壞字符算法
當出現一個壞字符時, BM算法向右移動模式串, 讓模式串中最靠右的對應字符與壞字符相對,然後繼續匹配。壞字符算法也有兩種情況。

Case1:模式串中有對應的壞字符時,見圖。


Case2:模式串中不存在壞字符。見圖。

 

移動規則
BM算法的移動規則是:

將概述中的++j,換成j+=MAX(shift(好後綴),shift(壞字符)),即

BM算法是每次向右移動模式串的距離是,按照好後綴算法和壞字符算法計算得到的最大值。

shift(好後綴)和shift(壞字符)通過模式串的預處理數組的簡單計算得到。好後綴算法的預處理數組是bmGs[],壞字符算法的預處理數組是BmBc[]。

3、代碼分析
定義
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]就沒有直接關係了。

 

代碼
view plaincopy to clipboardprint?
void preBmBc(char *x, int m, int bmBc[]) {  
 
   int i;  
 
   for (i = 0; i < ASIZE; ++i)  
 
      bmBc[i] = m;  
 
   for (i = 0; i < m - 1; ++i)  
 
      bmBc[x[i]] = m - i - 1;  
 
}  
 
void suffixes(char *x, int m, int *suff) {  
 
   int f, g, i;  
 
  f = 0;  
 
   suff[m - 1] = m;  
 
   g = m - 1;  
 
   for (i = m - 2; i >= 0; --i) {  
 
      if (i > g && suff[i + m - 1 - f] < i - g)  
 
         suff[i] = suff[i + m - 1 - f];  
 
      else {  
 
         if (i < g)  
 
            g = i;  
 
         f = i;  
 
         while (g >= 0 && x[g] == x[g + m - 1 - f])  
 
            --g;  
 
         suff[i] = f - g;  
 
      }  
 
   }  
 
}  
 
void preBmGs(char *x, int m, int bmGs[]) {  
 
   int i, j, suff[XSIZE];  
 
   suffixes(x, m, suff);  
 
   for (i = 0; i < m; ++i)  
 
      bmGs[i] = m;  
 
   j = 0;  
 
   for (i = m - 1; i >= 0; --i)  
 
      if (suff[i] == i + 1)  
 
         for (; j < m - 1 - i; ++j)  
 
            if (bmGs[j] == m)  
 
               bmGs[j] = m - 1 - i;  
 
   for (i = 0; i <= m - 2; ++i)  
 
      bmGs[m - 1 - suff[i]] = m - 1 - i;  
 
}  
 
void BM(char *x, int m, char *y, int n) {  
 
   int i, j, bmGs[XSIZE], bmBc[ASIZE];  
 
   /* Preprocessing */ 
 
   preBmGs(x, m, bmGs);  
 
   preBmBc(x, m, bmBc);  
 
   /* Searching */ 
 
   j = 0;  
 
   while (j <= n - m) {  
 
      for (i = m - 1; i >= 0 && x[i] == y[i + j]; --i);  
 
      if (i < 0) {  
 
         OUTPUT(j);  
 
         j += bmGs[0];  
 
      }  
 
      else 
 
         j += MAX(bmGs[i], bmBc[y[i + j]] - m + 1 + i);  
 
   }  
 


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/sealyao/archive/2009/09/18/4568167.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章