BM算法详解

BM算法详解

在用于查找子字符串的算法当中,BM(Boyer-Moore)算法匹配还是非常高效的,一般情况下,比KMP算法快好几倍。

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

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

  • 坏字符算法

  • 好后缀算法

坏字符算法

有下面两种情况

  • 坏字符没出现在模式串中,这时把模式串移动到坏字符的下一个字符,继续比较,如下图,字符’c’和字符’a’没有匹配上,字符’c’就坏字符,但坏字符’c’根本没有在模式串中,所以把模式串移到’c’字符后面继续匹配:
    BM算法详解_第1张图片

  • 坏字符在模式串中, BM算法向右移动模式串, 让模式串中最靠右的对应字符与坏字符相对,然后继续匹配,如下图:

BM算法详解_第2张图片

计算坏字符的方法

为了用代码来描述上述的两种情况,设计一个数组bmBc[‘k’],表示坏字符‘k’在模式串中出现的位置距离模式串末尾的最大长度,那么当遇到坏字符的时候,模式串可以移动距离为: shift(坏字符) = bmBc[T[i]]-(m-1-i)。shift(坏字符) 有可能是负数,这时候模式串可能会出现回退的情况,如下图:
BM算法详解_第3张图片


计算坏字符的代码实现

#define ASIZE 256
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;
}

对上面的代码简单的说明一下,ASIZE位256,表示了所有的字符,然后初始化所有所有字符在模式串总的未知位模式串的长度,最后从特征串后面往前遍历,计算特征串所有包含的字符里特征串尾部的长度。

好后缀算法

为了实现好后缀算法,需要定义一个数组suffix[],其中suffix[i] = s 表示以i为边界,与模式串后缀匹配的最大长度,如下图所示,用公式可以描述:满足P[i-s, i] == P[m-s, m]的最大长度s,m为特征串的长度。
BM算法详解_第4张图片

好后缀代码实现:

void suffixes(char *pattern, int pattern_len, int suffix[])
{
    int j;
    suffix[m-1] = m;
    for (i = pattern_len-2; i>=0; --i)
    {
        j = i;
        while(j >= 0&&pattern[j] == p[m-1 - (i - j)])
        {
            --j;
        }
        suffix[i] = i - j;
    }
}

void preBmGs(char *pattern, int pattern_len, int bmGs[]) {
     int i, j, suffix[XSIZE];
     suffixes(pattern, pattern_len, suffix);
     //都初始化为找不子串匹配到好后缀的
     for (i = 0; i < pattern_len; ++i)
        bmGs[i] = pattern_len; 
     j = 0;
     //最大前缀
     for (i = pattern_len - 1; i >= 0; --i)
        if (suff[i] == i + 1)
           for (; j < pattern_len - 1 - i; ++j)
             if (bmGs[j] == pattern_len)
                bmGs[j] = pattern_len - 1 - i;
    //模式串中有子串匹配上好后缀
    for (i = 0; i <= pattern_len - 2; ++i)
       bmGs[pattern_len - 1 - suff[i]] = pattern_len - 1 - i;
}

通过坏字符和好后缀算法,计算出了bmBc[]和bmGs[],查找匹配的时候取两着中大的跳转值,移动模式串。

你可能感兴趣的:(算法分析)