BM算法-字符串的模式匹配(一般情况下比KMP更高效的匹配算法)
思想:使用启发式规则,通过每次的失败的匹配尝试,算法都能根据这些信息来排除尽可能多的无法匹配的位置(为什么说是每次呢,因为每次的情况可能不一样,通过信息来跳过启发式规则的得到的步数)
目的:利用启发式规则,尽可能多跳过无法匹配的位置,从而达到更高的效率
方法:坏字符规则和好后缀规则,取其中移动步数较多的
坏字符规则:
1、当每轮匹配的时候,第一次出现模式串和主串不匹配的位置,在主串中对应位置的字符被成为坏字符。
2、遇到坏字符的处理方法:
(1)当坏字符没有出现在模式串中(可以提前对所有可能出现的字符集进行预处理,得到字符集对应在模式串的位置的一个数组,到时使用时再利用该数组。),则移动整个模式串到坏字符的下一个字符(模式串的第一个字符对准坏字符的下一个字符),实质是:模式串后移位数 = 坏字符的位置(在模式串的位置) - 模式串中的最右出现位置(坏字符在模式串最右出现的一个位置,如果没有出现在字符串是-1)
(2)坏字符在模式串中出现,模式串后移位数 = 坏字符的位置(在模式串的位置) - 模式串中的最右出现位置(坏字符在模式串最右出现的一个位置,如果没有出现在字符串是-1)
好后缀规则:
1、当每轮匹配的时候,第一次出现模式串和主串不匹配的位置,模式串从坏字符后面一个字符开始到最后的字符组成的后缀成为好后缀
2、遇到坏字符时,使用好后缀的处理方法:
(1)当好后缀没有再出现在模式串中,那么整个模式串就移动到好后缀对应的下一位,实质是:模式串后移位数 = 好后缀的位置(在模式串的最后的位置,其实就是模式串的长度) - 模式串中的最右出现位置(如果没有出现在字符串是-1);其实当出现babcdab,好后缀为dab的时候,其实就是没有再出现模式串中,直接就按没有情况来。
(2)当好后缀出现在模式串中,那么模式串后移位数 = 好后缀的位置(在模式串的最后的位置,其实就是模式串的长度) - 模式串中的最右出现位置(好后缀在模式串中最右出现的位置,但不能是好后缀的原位置,如果没有出现在字符串是-1);
之所以是最右,是因为避免跳过太多,比如模式串为babcb,如果主串为ddbabcbcd,在主串第二个b为好后缀的话,如果不是按照最右的来跳过,就会将匹配成功的主串最后部分的babcb给跳过了。
#include<iostream>
using namespace std;
#define SIZE 256 //定义字符集的数量,用于对坏字符进行预处理, 256是ascii字符集,不能用于处理带汉字的。想要处理带汉字的需要用到宽字符
//预处理坏字符
void badword(string str1,int badsite[]){
int i=0,ascii;
int len=str1.length();
//先用-1填充数组
for(;i<SIZE;i++){
badsite[i]=-1;
}
//将模式串的字符转换成ASCII值,再将数组下标为该ASCII值的赋上该字符再模式串的位置;然后需要使用的时候,只要查找坏字符对应的ASCII值为数组下标,对应的数组值就是坏字符在模式串的下标位置
for(i=0;i<len;i++){
ascii=int(str1[i]);
badsite[ascii]=i;
}
}
//预处理好后缀
void goodstring(string str1,int goodsite[]){
int i,j,k;
int len=str1.length();
//先用-1填充数组
for(i=0;i<len-1;i++){
goodsite[i]=-1;
}
//计算出长度为1~len(模式串总长度)后缀在模式串其他地方出现的位置(与后缀相同的字符串的最后字符下标)
for(i=0;i<len-1;i++){
j=i;
k=0;
while(j>=0&&str1[j]==str1[len-1-k]){
j--;
k++;
goodsite[k]=j+1;
}
}
}
//匹配函数
int algo_bm(string str1,string str2,int badsite[],int goodsite[]){
int len1=str1.length();
int len2=str2.length();
int i=len2-1,j=len2-1,movebad=0,movegood=0;
while(i<len1){
while(str1[i]==str2[j]&&j>=0){
i--;
j--;
}
if(j<0){
return i+1+1;//第一个+1是因为j<0,i肯定也--到了前一位,所以要加一补回来,第二个+1是因为数组是默认从0开始的,但最后要按照1开始的显示
}
movebad=j-badsite[int(str1[i])];
movegood=len2-goodsite[str2.substr(j+1,len2-j-1).length()]-1;
i=i+max(movebad,movegood);
j=len2-1;
}
return -1;
}
int main(){
string str1,str2;
cin>>str1>>str2;//不能带空格
int badsite[SIZE],goodsite[str2.length()];
badword(str2,badsite);
goodstring(str2,goodsite);
int site=algo_bm(str1,str2,badsite,goodsite);
if(site==-1){
cout<<"无法在主串中找到模式串"<<endl;
}else{
cout<<"模式串在主串的第"<< site<<"个位置匹配成功"<<endl;
}
return 0;
}