BM算法学习

BM算法全称为Boyer-Moore算法,与KMP算法相似是一种精确地模式匹配算法。一般来说比KMP算法更加高效。

   

KMP算法比较

     相似点:都是寻找模式串自身的规律,在匹配失效时获得最大的跳转距离;

     不同点:实际操作中KMP算法是从左到右进行比较,而BM算法是从右到左。这就决定了在寻找规律的过程中,KMP算法的核心是寻找子串的相同前缀,而BM算法是寻找相同的后缀。

        KMP算法的核心请参见之前的博文《KMP算法学习》:http://blog.csdn.net/myjoying/article/details/7947119

        BM算法的核心是两条启发式规则:坏字符规则和好后缀规则。

 

坏字符规则

       所谓坏字符就是在比较过程中发现的第一个不匹配的字符。如下图中的红色字符。

        T a  b  c d  a  b  c  a  b

        P   d  a  b c a  b

 

      坏字符规则:在任意字符x处发生失配时,在模式串P中找到与Tx左最近的x字符位置。并将找到的x移动到与Tx对齐。

       

      情况1P中存在这样x,则将两个x对齐,重新比较;

         BM算法学习_第1张图片

      情况2P中不存在这样的x,则将模式串P整个移动到Tx位置之后的位置对齐。

 BM算法学习_第2张图片

      

好后缀规则

       所谓好后缀就是在一次比较过程中模式串与目标串相匹配的后缀。如下图中的绿色字符串。

         T a  b  c  a  b  c  a  b

         P  d  a  b  a  b

        好后缀规则:如果程序匹配了一个好后缀,并且在模式中还有另外一个相同的后缀,那把下一个后缀移动到当前后缀位置。

        

        情况1:模式串中存在与好后缀完全匹配的子串,则将最靠右的那个子串移动到与T中好后缀对应位置,继续比较。

          BM算法学习_第3张图片

 

        情况2:在模式串中不存在与好后缀完全匹配的好后缀,则在好后缀子串中寻找与模式串前缀完全匹配的最长后缀。

          BM算法学习_第4张图片

移动距离

      BM算法存在两条移动规则,包括坏字符和好后缀规则。在每次失配时模式串移动的距离取两者最大距离移动。

 

 

代码分析(转)

 

BM算法子串比较失配时,按坏字符算法计算模式串需要向右移动的距离,要借助BmBc数组。

注意BmBc数组的下标是字符,而不是数字

BmBc数组的定义,分两种情况。

1、 字符在模式串中有出现。如下图,BmBc[‘k’]表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。

2、 字符在模式串中没有出现:,如模式串中没有字符p,则BmBc[‘p’] = strlen(模式串)。

wps_clip_image-1885

BM算法子串比较失配时,按好后缀算法计算模式串需要向右移动的距离,要借助BmGs数组。

BmGs数组的下标是数字,表示字符在模式串中位置。

BmGs数组的定义,分三种情况。

1、 对应好后缀算法case1:如下图:i是好后缀之前的那个位置。

BM算法学习_第5张图片

2、 对应好后缀算法case2:如下图所示:

BM算法学习_第6张图片

3、 当都不匹配时,BmGs[i] = strlen(模式串)

BM算法学习_第7张图片

在计算BmGc数组时,为提高效率,先计算辅助数组Suff。

Suff数组的定义:suff[i] = 以i为边界, 与模式串后缀匹配的最大长度,即P[i-s...i]=P[m-s…m]如下图:

BM算法学习_第8张图片

 

在计算BmGc数组时,为提高效率,先计算辅助数组Suff

Suff数组的定义:suff[i] =i为边界,与模式串后缀匹配的最大长度,即P[i-s...i]=P[m-sm]如下图:

 

Suff[]计算BmGs的方法:

1 BmGs[0m-1] = m;(第三种情况)

2)计算第二种情况下的BmGs[]值:

[cpp] view plain copy print ?
  1. for (i = m - 1; i >= 0; --i)    
  2.           
  3.     if (suff[i] == i + 1)    
  4.               
  5.         for (; j < m - 1 - i; ++j)    
  6.                   
  7.             if (bmGs[j] == m)    
  8.                       
  9.                 bmGs[j] = m - 1 - i;   

 

3)计算第一种情况下BmGs[]值,可以覆盖前两种情况下的BmGs[]值:

 

[cpp] view plain copy print ?
  1. for (i = 0; i <= m - 2; ++i)    
  2.                       
  3.     bmGs[m - 1 - suff[i]] = m - 1 - i;  


 

 

如下图所示:

BM算法学习_第9张图片

Suff[]数组的计算方法。

常规的方法:如下,很裸很暴力。

[cpp] view plain copy print ?
  1. Suff[m-1]=m;  
  2. for(i=m-2;i>=0;--i){  
  3.    q=i;  
  4.    while(q>=0&&P[q]==P[m-1-i+q])  
  5.    --q;  
  6.   Suff[i]=i-q;  
  7. }  

 

有聪明人想出一种方法,对常规方法进行改进。基本的扫描都是从右向左。改进的地方就是利用了已经计算得到的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]就没有直接关系了。

BM算法学习_第10张图片

 

 

[cpp] view plain copy print ?
  1. #include <stdio.h>   
  2.   
  3. #define ASIZE 26   
  4. #define XSIZE 6   
  5.   
  6. //坏字符规则计算   
  7. void preBmBc(char *x, int m, int bmBc[]) {    
  8.     int i;    
  9.     for (i = 0; i < ASIZE; ++i)           
  10.         bmBc[i] = m;    
  11.       
  12.     for (i = 0; i < m - 1; ++i)      
  13.         bmBc[x[i]-'a'] = m - i - 1;      
  14. }   
  15. //stuff辅助数组计算      
  16. void suffixes(char *x, int m, int *suff)   
  17. {      
  18.     int f, g, i;     
  19.     f = 0;            //上次匹配成功的位置   
  20.     suff[m - 1] = m;     
  21.     g = m - 1;        //上次匹配失败的位置   
  22.     for (i = m - 2; i >= 0; --i) {    
  23.           
  24.         if (i > g && suff[i + m - 1 - f] < i - g)    
  25.               
  26.             suff[i] = suff[i + m - 1 - f];      
  27.         else {    
  28.               
  29.             if (i < g)    
  30.                   
  31.                 g = i;    
  32.               
  33.             f = i;    
  34.               
  35.             while (g >= 0 && x[g] == x[g + m - 1 - f]) //匹配成功则一直匹配    
  36.                   
  37.                 --g;    
  38.               
  39.             suff[i] = f - g;    
  40.               
  41.         }    
  42.           
  43.     }    
  44.       
  45. }    
  46. //好后缀规则实现   
  47. void preBmGs(char *x, int m, int bmGs[]) {    
  48.       
  49.     int i, j, suff[XSIZE];    
  50.       
  51.     suffixes(x, m, suff);      
  52.     for (i = 0; i < m; ++i)  //情况3   
  53.         bmGs[i] = m;    
  54.       
  55.     j = 0;    
  56.       
  57.     for (i = m - 1; i >= 0; --i)   //情况2    
  58.           
  59.         if (suff[i] == i + 1)    
  60.               
  61.             for (; j < m - 1 - i; ++j)    
  62.                   
  63.                 if (bmGs[j] == m)    
  64.                       
  65.                     bmGs[j] = m - 1 - i;    
  66.                   
  67.     for (i = 0; i <= m - 2; ++i)    //情况1     
  68.                       
  69.         bmGs[m - 1 - suff[i]] = m - 1 - i;    
  70.                   
  71. }    
  72.   
  73. int MAX(int a, int b)  
  74. {  
  75.     return a>b? a: b;  
  76. }  
  77.   
  78. void print(int *t, int m)  
  79. {  
  80.     for (int i=0; i<m; i++)  
  81.     {  
  82.         printf("%d ", t[i]);  
  83.     }  
  84.     printf("\n");  
  85. }  
  86. //BM算法主体部分,x为模式串,y为目标串   
  87. void BM(char *x, int m, char *y, int n) {    
  88.       
  89.     int i, j, bmGs[XSIZE], bmBc[ASIZE];    
  90.       
  91.     /* Preprocessing */    
  92.         
  93.       
  94.     preBmBc(x, m, bmBc);    
  95.     print(bmBc, ASIZE);  
  96.   
  97.     preBmGs(x, m, bmGs);  
  98.     print(bmGs, XSIZE);  
  99.       
  100.     /* Searching */    
  101.       
  102.     j = 0;    
  103.       
  104.     while (j <= n - m) {    
  105.           
  106.         for (i = m - 1; i >= 0 && x[i] == y[i + j]; --i);    
  107.           
  108.         if (i < 0) {    
  109.               
  110.             printf("%d ",j);  
  111.               
  112.             j += bmGs[0];    
  113.               
  114.         }    
  115.           
  116.         else    
  117.               
  118.             j += MAX(bmGs[i], bmBc[y[i + j]-'a'] - m + 1 + i);    
  119.           
  120.     }     
  121. }    
  122.   
  123. int main()  
  124. {  
  125.     char target[11] = "iabcdabcab";  
  126.     char pattern[XSIZE+1] = "dabcab";  
  127.   
  128.     BM(pattern, XSIZE, target, 10);  
  129.   
  130.     return 0;  
  131. }  


 

 

 

你可能感兴趣的:(BM算法学习)