• BM 字符串匹配


    BM字符串匹配算法

    http://www.cnblogs.com/dsky/archive/2012/05/04/2483190.html

    在用于查找子字符串的算法当中,BM(Boyer-Moore)算法是目前相当有效又容易理解的一种,一般情况下,比KMP算法快3-5倍。

    BM算法在移动模式串的时候是从左到右,而进行比较的时候是从右到左的。

    BM算法实际上包含两个并行的算法,坏字符算法和好后缀算法。这两种算法的目的就是让模式串每次向右移动尽可能大的距离(j+=x,x尽可能的大)。 

    几个定义:

    例主串和模式串如下:

    主串  :  mahtavaatalomaisema omalomailuun

    模式串: maisemaomaloma

    好后缀:模式串中的aloma为“好后缀”。

    坏字符:主串中的“t”为坏字符。

    好后缀算法

    如果程序匹配了一个好后缀, 并且在模式中还有另外一个相同的后缀, 那

    把下一个后缀移动到当前后缀位置。好后缀算法有两种情况:

    Case1:模式串中有子串和好后缀安全匹配,则将最靠右的那个子串移动到好后缀的位置。继续进行匹配。

    wps_clip_image-979

    Case2:如果不存在和好后缀完全匹配的子串,则在好后缀中找到具有如下特征的最长子串,使得P[m-s…m]=P[0…s]。说不清楚的看图。

     wps_clip_image-1152

    坏字符算法

    当出现一个坏字符时, BM算法向右移动模式串, 让模式串中最靠右的对应字符与坏字符相对,然后继续匹配。坏字符算法也有两种情况。

    Case1:模式串中有对应的坏字符时,见图。 
    wps_clip_image-1349

    Case2:模式串中不存在坏字符。见图。

    wps_clip_image-1472

     移动规则

    BM算法的移动规则是:

    将概述中的++j,换成j+=MAX(shift(好后缀),shift(坏字符)),即

    BM算法是每次向右移动模式串的距离是,按照好后缀算法和坏字符算法计算得到的最大值。

    shift(好后缀)和shift(坏字符)通过模式串的预处理数组的简单计算得到。好后缀算法的预处理数组是bmGs[],坏字符算法的预处理数组是BmBc[]。

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

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

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

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

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

    wps_clip_image-1885

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

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

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

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

    wps_clip_image-2036

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

    wps_clip_image-2086

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

    wps_clip_image-2145

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

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

    wps_clip_image-2272

    举例如下:

     

    wps_clip_image-2380

     分析

    用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;

    如下图所示:

    wps_clip_image-2697

    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]就没有直接关系了。

    wps_clip_image-3177

    复制代码
     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 }
    复制代码

     

  • 相关阅读:
    QT学习笔记——06-Day16_C++_QT
    QT学习笔记——06-Day15_C++_QT
    QT学习笔记——06-Day14_C++_QT
    c++提高学习笔记——05-c++STLday13_贪吃蛇案例
    c++提高学习笔记——05-c++STLday12
    c++提高学习笔记——05-c++STLday11
    混子的 后缀数组 刷题记录
    POJ2528Mayor's posters 线段树,离散化技巧
    莫比乌斯反演 刷题记录
    P问题,NP问题,NPC问题学习笔记
  • 原文地址:https://www.cnblogs.com/threef/p/2994762.html
Copyright © 2020-2023  润新知