前提:
首先说明一点:这篇文章内的算法完完全全没有考虑复杂度,一切都是因为我在调试坏字符时发现了个bug(只找模式串某个字符在最右边出现的位置是不够的),所以目标是为了按照这个思路能够解决问题。当然这样做复杂度会巨高,不然Boyer和Moore也就不搞好后缀规则了。
思路:
我们先把问题说的明白点,相信进来的朋友应该都遇到过:
当我们匹配模式串和目标串匹配了好几个的时候,如图:此时目标串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("没找到");
}
}
(主要方法)
(各位画个图吧,很直观的。我这个ppt作图不太熟练)
然后关于好后缀的,它的主要思路很简单,就是移动到后缀能重复的位置嘛,但这个过程并不轻松。 bm算法给出了suffix数组表示各种长度的后缀在模式串前面部分出现的位置,也给出了prefix数组表示对于模式串开头有无可能有子串和后缀匹配。
具体写法参考了cadn博主(blog.csdn.net/weixin_4627…