和Horspool不同的是,算法在失配时不光要看那个不配对的字符,而且还要看已经有多少个字符成功配对,把这两个信息相结合得出模式需要移动的距离。此算法在开始匹配之前需要建立两个表:坏符号移动表和好后缀移动表。坏符号移动表的求法和Horspool的求法一样,但好后缀移动表的计算就有一点复杂了。
运行结果:
关于BM算法,这里有一篇好文章,帮助快速理解:
代码如下:
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
// 计算坏符号移动表
void Get_Bad_Char_Table(const string &text, const string &pattern, map<char, int> &next)
{
int text_len = text.size();
int pattern_len = pattern.size();
for (int i = 0; i < text_len; i++)
next.insert(make_pair(text[i], pattern_len));
for (int i = 0; i < pattern_len - 1; i++)
next[pattern[i]] = pattern_len - 1 - i;
}
// 计算好后缀移动表
void Get_Good_Suffix_Table(const string &pattern, vector<int> &good_suffix)
{
int pattern_len = pattern.size();
const char *p_begin = pattern.c_str();
int save_index, save_len;
bool is_first = true;
for (int i = pattern_len - 1; i > 0; i--)
{
save_index = save_len = -1;
for (int j = i; j < pattern_len; j++)
{
int suffix_len = pattern_len - j;
if (suffix_len == 1)
{
for (int k = 0; k < pattern_len - suffix_len; k++)
{
if (strncmp(p_begin + k, p_begin + j, suffix_len) == 0)
{
save_index = k;
save_len = suffix_len;
if (is_first == false)
break;
}
}
if (is_first == true)
is_first = false;
}
else
{
for (int k = 0; k < pattern_len - suffix_len; k++)
{
if (strncmp(p_begin + k, p_begin + j, suffix_len) == 0)
{
save_index = k;
save_len = suffix_len;
break;
}
}
if (save_index != -1 && save_len != -1)
break;
}
}
if (save_index != -1 && save_len != -1)
good_suffix.push_back(pattern_len - save_index - save_len);
else
good_suffix.push_back(pattern_len);
}
}
void BM(const string &text, const string &pattern, vector<int> &result)
{
int text_len = text.size();
int pattern_len = pattern.size();
map<char, int> bad_char; // 保存坏字符的表
vector<int> good_suffix; // 保存好后缀的表
Get_Bad_Char_Table(text, pattern, bad_char);
Get_Good_Suffix_Table(pattern, good_suffix);
for (int i = pattern_len - 1; i < text_len; /* NULL */)
{
int text_pos = i;
int pattern_pos;
int suffix_len = 0; // 匹配字符数
for (pattern_pos = pattern_len - 1; pattern_pos >= 0; /* NULL */)
{
if (text[text_pos] == pattern[pattern_pos])
{
text_pos--;
pattern_pos--;
suffix_len++;
}
else
{
if (suffix_len == 0) // 和Horspool算法相同
{
i += bad_char[text[i]];
break;
}
else
{
int d1 = bad_char[text[text_pos]] - suffix_len;
int d2 = good_suffix[suffix_len - 1]; // 注意这里的-1
i += max(d1, d2);
break;
}
}
}
if (pattern_pos < 0)
{
result.push_back(i - pattern_len + 1); // 完全匹配时的起始位置
i++;
}
}
}
int main()
{
string text = "hello world good google Nestle people google hello this is a test google";
string pattern = "google";
vector<int> result;
BM(text, pattern, result);
int cnt = 1;
for (vector<int>::iterator iter = result.begin(); iter != result.end(); ++iter)
cout << "match " << cnt++ << " = " << *iter << endl;
system("pause");
return 0;
}
运行结果:
效率分析:
BM算法的最差效率是线性的,当字母表规模很大时,速度非常快。
参考:
《算法设计与分析基础》 P197-P201.