字符串匹配的bm算法,只用坏字符规则可以实现吗?

94 阅读3分钟

前提:

首先说明一点:这篇文章内的算法完完全全没有考虑复杂度,一切都是因为我在调试坏字符时发现了个bug(只找模式串某个字符在最右边出现的位置是不够的),所以目标是为了按照这个思路能够解决问题。当然这样做复杂度会巨高,不然Boyer和Moore也就不搞好后缀规则了。

思路:

我们先把问题说的明白点,相信进来的朋友应该都遇到过:

image.png

当我们匹配模式串和目标串匹配了好几个的时候,如图:此时目标串k位和模式串i位不等,那么如果单纯的把想把模式串移动到最右边b出现的位置,反而会出现反向移动。这个问题表明,我们在模式串中找坏字符所对应的位置时,不是仅仅找最右边的那个,而是对于不同的位置i,找其左边子串中出现的,最右边的那个。 (在图中表示就是:在模式串中Q的左边找第一个b)。

我们关于这个需求,想想要得到什么,又要传入什么做参数。

问题的完整表述是:我们要的是目标串中第k位的b对应的,在模式串中的第i位之前最近的b。

我们可以只把在模式串中出现过的所有字符,求出在1-i间每个位置之前的最近字符串。这样写需要传入特定字符和特定索引,得到一个索引,如果像以前的思路求出一个对应表的话:可以先hashMap,k是char,value是一个数组,数组中再用索引来找。

也可以直接用目标串中的k从目标串得到这个字符,然后配合模式串中的索引i,在主方法体内多次找,而并不提前做一个表。

我为了简单直观用第二种实现了: 先放代码:

public int getBadChar(String moshi,String mubiao, int i ,int k){
    String subS=moshi.substring(0,i);  //先切出i之前的部分,再对此倒着找字符
    int location=-1;  //没有找到对应值时的默认值
    int targetChar=mubiao.charAt(k);
    for (int j = subS.length()-1; j >=0 ; j--) {
        if(subS.charAt(j)==targetChar){
            location=j;
            break;
        }
    }
    return location;
}

(在字符串匹配的过程中用索引i和k得到要移动到的位置)

public void bmSolver(String moshi,String mubiao){
    int i=moshi.length()-1;   //i为模式串中的索引
    int k=moshi.length()-1;   //k为目标串中的索引
    int modCount=0;  //这个是用来记录有几位匹配了,也就是k在这次匹配中前移了几次
    while(k<mubiao.length()&& i>=0){
        if(mubiao.charAt(k)==moshi.charAt(i)){
            i--;
            k--;
            modCount++;
        }
        else{
            int right=i-getBadChar(moshi,mubiao,i,k)+modCount;
            //right表示k右移的量,其中i-getBadChar是模式串右移的量。具体见下图
            k+=right;
            i=moshi.length()-1;
            modCount=0;
        }
    }
    if(i<0){
        System.out.println("找到");
    }
    if(k>=mubiao.length()){
        System.out.println("没找到");
    }
}

(主要方法)

image.png

image.png

(各位画个图吧,很直观的。我这个ppt作图不太熟练)

然后关于好后缀的,它的主要思路很简单,就是移动到后缀能重复的位置嘛,但这个过程并不轻松。 bm算法给出了suffix数组表示各种长度的后缀在模式串前面部分出现的位置,也给出了prefix数组表示对于模式串开头有无可能有子串和后缀匹配。

具体写法参考了cadn博主(blog.csdn.net/weixin_4627…