模式匹配BM算法介紹與實現

 

如下是網上找來的,我整理了下,我的以爲寫的挺好的,分享給你們。web

1、介紹
 BM算法:是一種精確字符串匹配算法,其基本思路採用從右向左比較的方法,其中用到了「壞字符規則」和「好後綴規則」,經過這兩個規則來決定向右跳躍的距離。算法

2、算法流程
 BM算法的基本流程:設文本串T,模式串爲P。先將T、P左對齊,而後從右往左依次逐個字符比較,若是發現不匹配,則使用前面提到的兩個規則來計算獲得跳躍的距離,
一直到匹配結束或者匹配成功。ide

3、概念介紹
 在匹配中已經匹配的部分稱爲好後綴,第一個不匹配的字符成爲壞字符。
 1).壞字符規則
  在BM算法從右向左掃描的過程當中,若發現某個字符x不匹配,則按以下兩種狀況討論: 函數

               i.  若是字符x在模式P中沒有出現,那麼從字符x開始的m個文本顯然不可能與P匹配成功,直接所有跳過該區域便可。 性能

               ii. 若是x在模式P中出現,則以該字符進行對齊。 spa

 

         用數學公式表示,設Skip(x)爲P右移的距離,m爲模式串P的長度,max(x)爲字符x在P中最右位置。 orm

 


 2).好後綴規則
         若發現某個字符不匹配的同時,已有部分字符匹配成功,則按以下兩種狀況討論: blog

              i.  若是在P中位置t處已匹配部分P'在P中的某位置t'也出現,且位置t'的前一個字符與位置t的前一個字符不相同,則將P右移使t'對應t方纔的所在的位置。 ip

              ii. 若是在P中任何位置已匹配部分P'都沒有再出現,則找到與P'的後綴P''相同的P的最長前綴x,向右移動P,使x對應方纔P''後綴所在的位置。   ci

 

         用數學公式表示,設Shift(j)爲P右移的距離,m爲模式串P的長度,j 爲當前所匹配的字符位置,s爲t'與t的距離(以上狀況i)或者x與P''的距離(以上狀況ii)。

      

在BM算法匹配的過程當中,取SKip(x)與Shift(j)中的較大者做爲跳躍的距離。

4、代碼實現

  1. /* 
  2.     函數:int* MakeSkip(char *, int) 
  3.     目的:根據壞字符規則作預處理,創建一張壞字符表 
  4.     參數: 
  5.         ptrn => 模式串P 
  6.         PLen => 模式串P長度 
  7.     返回: 
  8.         int* - 壞字符表 
  9. */  
  10. int* MakeSkip(char *ptrn, int pLen)  
  11. {     
  12.     int i;  
  13.     //爲創建壞字符表,申請256個int的空間  
  14.     /*PS:之因此要申請256個,是由於一個字符是8位, 
  15.       因此字符可能有2的8次方即256種不一樣狀況*/  
  16.     int *skip = (int*)malloc(256*sizeof(int));  
  17.   
  18.     if(skip == NULL)  
  19.     {  
  20.         fprintf(stderr, "malloc failed!");  
  21.         return 0;  
  22.     }     
  23.   
  24.     //初始化壞字符表,256個單元所有初始化爲pLen  
  25.     for(i = 0; i < 256; i++)  
  26.     {  
  27.         *(skip+i) = pLen;  
  28.     }  
  29.   
  30.     //給表中須要賦值的單元賦值,不在模式串中出現的字符就不用再賦值了  
  31.     while(pLen != 0)  
  32.     {  
  33.         *(skip+(unsigned char)*ptrn++) = pLen--;  
  34.     }  
  35.   
  36.     return skip;  
  37. }  
  38.   
  39.   
  40. /* 
  41.     函數:int* MakeShift(char *, int) 
  42.     目的:根據好後綴規則作預處理,創建一張好後綴表 
  43.     參數: 
  44.         ptrn => 模式串P 
  45.         PLen => 模式串P長度 
  46.     返回: 
  47.         int* - 好後綴表 
  48. */  
  49. int* MakeShift(char* ptrn,int pLen)  
  50. {  
  51.     //爲好後綴表申請pLen個int的空間  
  52.     int *shift = (int*)malloc(pLen*sizeof(int));  
  53.     int *sptr = shift + pLen - 1;//方便給好後綴表進行賦值的指標  
  54.     char *pptr = ptrn + pLen - 1;//記錄好後綴表邊界位置的指標  
  55.     char c;  
  56.   
  57.     if(shift == NULL)  
  58.     {  
  59.         fprintf(stderr,"malloc failed!");  
  60.         return 0;  
  61.     }  
  62.   
  63.     c = *(ptrn + pLen - 1);//保存模式串中最後一個字符,由於要反覆用到它  
  64.   
  65.     *sptr = 1;//以最後一個字符爲邊界時,肯定移動1的距離  
  66.   
  67.     pptr--;//邊界移動到倒數第二個字符(這句是我本身加上去的,由於我總以爲不加上去會有BUG,你們試試「abcdd」的狀況,即末尾兩位重複的狀況)  
  68.   
  69.     while(sptr-- != shift)//該最外層循環完成給好後綴表中每個單元進行賦值的工做  
  70.     {  
  71.         char *p1 = ptrn + pLen - 2, *p2,*p3;  
  72.           
  73.         //該do...while循環完成以當前pptr所指的字符爲邊界時,要移動的距離  
  74.         do{  
  75.             while(p1 >= ptrn && *p1-- != c);//該空循環,尋找與最後一個字符c匹配的字符所指向的位置  
  76.               
  77.             p2 = ptrn + pLen - 2;  
  78.             p3 = p1;  
  79.               
  80.             while(p3 >= ptrn && *p3-- == *p2-- && p2 >= pptr);//該空循環,判斷在邊界內字符匹配到了什麼位置  
  81.   
  82.         }while(p3 >= ptrn && p2 >= pptr);  
  83.   
  84.         *sptr = shift + pLen - sptr + p2 - p3;//保存好後綴表中,以pptr所在字符爲邊界時,要移動的位置  
  85.         /* 
  86.           PS:在這裏我要聲明一句,*sptr = (shift + pLen - sptr) + p2 - p3; 
  87.              你們看被我用括號括起來的部分,若是隻須要計算字符串移動的距離,那麼括號中的那部分是不須要的。 
  88.              由於在字符串自左向右作匹配的時候,指標是一直向左移的,這裏*sptr保存的內容,實際是指標要移動 
  89.              距離,而不是字符串移動的距離。我想SNORT是出於性能上的考慮,才這麼作的。           
  90.         */  
  91.   
  92.         pptr--;//邊界繼續向前移動  
  93.     }  
  94.   
  95.     return shift;  
  96. }  
  97.   
  98.   
  99. /* 
  100.     函數:int* BMSearch(char *, int , char *, int, int *, int *) 
  101.     目的:判斷文本串T中是否包含模式串P 
  102.     參數: 
  103.         buf => 文本串T 
  104.         blen => 文本串T長度 
  105.         ptrn => 模式串P 
  106.         PLen => 模式串P長度 
  107.         skip => 壞字符表 
  108.         shift => 好後綴表 
  109.     返回: 
  110.         int - 1表示成功(文本串包含模式串),0表示失敗(文本串不包含模式串)。 
  111. */  
  112. int BMSearch(char *buf, int blen, char *ptrn, int plen, int *skip, int *shift)  
  113. {  
  114.     int b_idx = plen;    
  115.     if (plen == 0)  
  116.         return 0;  
  117.     while (b_idx <= blen)//計算字符串是否匹配到了盡頭  
  118.     {  
  119.         int p_idx = plen, skip_stride, shift_stride;  
  120.         while (buf[--b_idx] == ptrn[--p_idx])//開始匹配  
  121.         {  
  122.             if (b_idx < 0)  
  123.                 return -1;  
  124.             if (p_idx == 0)  
  125.             {       
  126.                 return b_idx ;  
  127.             }  
  128.         }  
  129.         skip_stride = skip[(unsigned char)buf[b_idx]];//根據壞字符規則計算跳躍的距離  
  130.         shift_stride = shift[p_idx];//根據好後綴規則計算跳躍的距離  
  131.         b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;//取大者  
  132.     }  
  133.     return -1;  
  134. }  
相關文章
相關標籤/搜索