BM算法采用了尾部比较的方法,具体来说就是:字符串与搜索词头部对齐,从尾部开始搜索,然后直接从尾部比较。
其定义了坏字符规则好后缀规则

坏字符规则

image.png

两者首次匹配时,尾部S与E不匹配,则称S为坏字符,此时在搜索词中无法搜索到坏字符S,因此直接将搜索词移动到S后
image.png

若坏字符P在搜索词中能够找到,则将字符串与搜索词中的P对齐。
因此坏字符规则可总结为:

  1. 若搜索词中存在坏字符,则两者对齐
  2. 若搜索词中不存在坏字符,则直接移动到坏字符后
    用公式表示为:

    后移位数 = 坏字符的位置 - 搜索词中的上一次出现位置

若坏字符不存在与搜索词中,则上次出现位置为-1
其实就是:后移位数 = 搜索词长度 - 搜索词中上次出现的位置。上次出现的位置是从右往左寻找的。如坏字符B与搜索词ABABA,则上次出现的位置为从右往左的第二位B

好后缀规则

image.png
**若字符串MPLE与搜索词匹配,则MPLE成为好后缀

IA不匹配,则I为坏字符,按照坏字符规则将移动 2-(-1)= 3位,但若按照好后缀规则:

后移位数 = 好后缀的位置 - 搜索词中的上一次出现位置

则移动位数为6 - 0 = 6位
所谓好后缀规则就是:

  1. 若好后缀在搜索词中出现,则将两者对齐,若好后缀出现多次,则与最右侧的那个对齐
  2. 若好后缀只出现一次,则寻找其最大子串作为好后缀。

接着取好后缀与坏字符中移动步数较大者作为最后移动步数。


  1. // a,b表示主串和模式串;n,m表示主串和模式串的长度。
  2. public int bm(char[] a, int n, char[] b, int m) {
  3. int[] bc = new int[SIZE]; // 记录模式串中每个字符最后出现的位置
  4. generateBC(b, m, bc); // 构建坏字符哈希表
  5. int[] suffix = new int[m];
  6. boolean[] prefix = new boolean[m];
  7. generateGS(b, m, suffix, prefix);
  8. int i = 0; // j表示主串与模式串匹配的第一个字符
  9. while (i <= n - m) {
  10. int j;
  11. for (j = m - 1; j >= 0; --j) { // 模式串从后往前匹配
  12. if (a[i+j] != b[j]) break; // 坏字符对应模式串中的下标是j
  13. }
  14. if (j < 0) {
  15. return i; // 匹配成功,返回主串与模式串第一个匹配的字符的位置
  16. }
  17. int x = j - bc[(int)a[i+j]];
  18. int y = 0;
  19. if (j < m-1) { // 如果有好后缀的话
  20. y = moveByGS(j, m, suffix, prefix);
  21. }
  22. i = i + Math.max(x, y);
  23. }
  24. return -1;
  25. }
  26. // j表示坏字符对应的模式串中的字符下标; m表示模式串长度
  27. private int moveByGS(int j, int m, int[] suffix, boolean[] prefix) {
  28. int k = m - 1 - j; // 好后缀长度
  29. if (suffix[k] != -1) return j - suffix[k] +1;
  30. for (int r = j+2; r <= m-1; ++r) {
  31. if (prefix[m-r] == true) {
  32. return r;
  33. }
  34. }
  35. return m;
  36. }

c++

  1. //BM算法:字符串搜索
  2. #include "stdafx.h"
  3. #include<iostream>
  4. #include<string>
  5. #include<vector>
  6. using namespace std;
  7. vector<int> preBmBc(string ps);
  8. int main()
  9. {
  10. string ts = "HERE IS A SIMPLE EXAMPLE";
  11. string ps = "EXAMPLE";
  12. vector<int> matched;
  13. vector<int> BC = preBmBc(ps);//创建bad character数组
  14. //原始字符串与模式串长度
  15. int tlen = ts.size();
  16. int plen = ps.size();
  17. int tindex = 0;//ts索引
  18. while (tindex+plen <= tlen) {
  19. int badmove = 0;
  20. int goodmove = 0;
  21. for (size_t j = plen; j > 0; j--)
  22. {
  23. if (ts[tindex + j - 1] != ps[j - 1]) {
  24. badmove = BC[ts[tindex+j-1]]; //bad character移动步数
  25. cout <<"Move step:"<< badmove << endl;
  26. break;
  27. }
  28. if (j == 1) {//匹配到
  29. matched.push_back(tindex);
  30. tindex += plen - 1;
  31. continue;
  32. }
  33. }
  34. tindex += badmove;
  35. }
  36. for (size_t i = 0; i < matched.size(); i++)
  37. {
  38. cout << "matched at:" << matched[i] << endl;
  39. }
  40. system("pause");
  41. return 0;
  42. }
  43. //bad character数组,256
  44. vector<int> preBmBc(string ps) {
  45. vector<int> BC(256, ps.size());
  46. for (size_t i = 0; i < ps.size(); i++)
  47. {
  48. BC[ps[i]] = ps.size() - i - 1;
  49. }
  50. return BC;
  51. }

参考

https://juejin.im/post/5c87512f6fb9a049f571fcb5