Boyer-Moore算法

Boyer-Moore算法

一.两大规则

(1)坏字符规则

1)如果坏字符不存在于模式串中,直接将整个模式串拉到坏字符下一个字符,也就是说将模式串的第一个字符与主串中坏字符的下一个字符对齐

2)如果坏字符存在与模式串中,将模式串中最后那个与坏字符相同的字符与坏字符对齐

(2)好后缀规则

当模式串与主串从后往前有多个已经匹配上后出现坏字符就称后面那些匹配上的为好后缀

1)如果模式串从前往后存在与好后缀相同的字串,就将字串与该好后缀对齐,若有多个则选择最后面那个。

2)如果不存在则先比较好后缀中是否存在后缀子串与模式串的前缀相同,若存在则将模式串 前缀 与好后缀 的后缀子串 对齐,若不一样则将 模式串第一个字符与好后缀后一个字符对齐

二.代码思想

遇到坏字符后分别计算在坏字符规则和好字符规则下的模式串移动距离,并选择二者中较大的作为模式串的移动距离

三.模板代码

#include <algorithm>
#include <string>
#include <iostream>

using namespace std;
#define SIZE 256    //字符集字符数
void generateBadChar(char *b,int m,int *badchar)//初始化哈希表 
{
    int i,ascii;
    for(i=0;i<SIZE;++i) badchar[i]=-1;
    for(i=0;i<m;++i) badchar[int(b[i])]=i;//保证会记录最后出现的下标
}
void generateGS(char *b,int m,int *suffix,bool *prefix)
//预处理模式串,b为模式串,m为模式串的长度,suffix[i]记录长度为i的后缀在模式串中最后出现时第一个字符的下标,prefix[i]表示长度为i的后缀是否存在与它相同的前缀 
{
    int i,j,k;
    for(i=0;i<m;i++){//对两个数组做初始化 
        suffix[i]=-1;
        prefix[i]=false;
    } 
    for(i=0;i<m-1;++i){
        j=i,k=0;//k为后缀的长度 
        while(j>=0&&b[j]==b[m-1-k])//一个一个字符进行比较
        {
            --j,++k; 
            suffix[k]=j+1;
            //相同后缀子串长度为k时,该子串在b[0,i]中的起始下标
            // (如果有多个相同长度的子串,被赋值覆盖,存较大的)
        } 
        if(j==-1)//也就是说从i到0都匹配
             prefix[k] = true;//后缀子串也是模式串的前缀子串
    } 
} 
int moveByGS(int j, int m, int *suffix, bool *prefix)//传入的j是坏字符对应的模式串中的字符下标
{
    int k=m-1-j;//好后缀的长度
    if(suffix[k]!=-1)//找到跟好后缀一样的模式子串
        return j-suffix[k]+1;
    for(int r = j + 2; r < m; ++r)
    {
        if(prefix[m-r] == true)//m-r是好后缀的子串的长度,如果这个好后缀的子串是模式串的前缀子串
            return r;//在上面没有找到相同的好后缀下,移动r位,对齐前缀到好后缀
    }
    return m;//都没有匹配的,移动m位(模式串长度)
} 
int str_bm(char *a, int n, char *b, int m)//a表示主串,长n; b表示模式串,长m
{
    int *badchar = new int [SIZE];//记录模式串中每个字符最后出现的位置
    generateBadChar(b,m,badchar);     //构建坏字符哈希表
    int *suffix = new int [m];//某个后缀从前往后找最后一次出现时第一个字符的下标 
    bool *prefix = new bool [m];//某个后缀字串如果也为前缀字串 
    generateGS(b, m, suffix, prefix);   //预处理模式串,填充suffix,prefix
    int i = 0, j, moveLen1, moveLen2;//j表示主串与模式串匹配的第一个字符
    while(i < n-m+1)
    {
        for(j = m -1; j >= 0; --j)  //模式串从后往前匹配
        {
            if(a[i+j] != b[j])
                break;  //坏字符对应模式串中的下标是j
        }
        if(j < 0)   //匹配成功
        {
            delete [] badchar;
            delete [] suffix;
            delete [] prefix;
            return i;   //返回主串与模式串第一个匹配的字符的位置
        }
        //这里等同于将模式串往后滑动 j-badchar[int(a[i+j])] 位
        moveLen1 = j - badchar[int(a[i+j])];//按照坏字符规则移动距离
        moveLen2 = 0;
        if(j < m-1)//如果有好后缀的话
        {
            moveLen2 = moveByGS(j,m,suffix,prefix);//按照好后缀规则移动距离
        }
        i = i + max(moveLen1,moveLen2);//取大的移动
    }
    delete [] badchar;
    delete [] suffix;
    delete [] prefix;
    return -1;
}
int main()
{
    string a , b;
    cout<<"请输入主串:";
    cin>>a;
    cout<<"请输入模式串:";
    cin>>b;
    cout << a << "中第一次出现" << b << "的位置(从0开始)是:" << str_bm(&a[0],a.size(),&b[0],b.size());
    return 0;
}
 

猜你喜欢

转载自www.cnblogs.com/PokimonMaster/p/12188858.html