Boyer-Moore 字符串匹配算法,比 KMP 更快!!!

​1. 算法简介​

Boyer-Moore 算法是一种高效的​​字符串匹配算法​​,用于在文本(Text)中快速查找模式(Pattern)的位置。其核心思想是:

  • ​从右向左​​比较字符(与常规从左向右相反)。
  • 利用​​坏字符规则(Bad Character Rule)​​和​​好后缀规则(Good Suffix Rule)​​跳过不必要的比较,大幅减少匹配次数。

​2. 核心规则​

​(1) 坏字符规则(Bad Character Rule)​
  • 当发现文本(T)中的某个字符与模式(P)不匹配时:
    • 若该字符在模式中​​不存在​​,则模式直接跳过该字符。
    • 若该字符在模式中​​存在​​,则将模式向右移动,​​对齐模式中该字符的最后出现位置​​。
​(2) 好后缀规则(Good Suffix Rule)​
  • 当发现不匹配时,若已有部分后缀匹配成功:
    • 在模式中查找该后缀的其他出现位置,并移动模式对齐。
    • 若不存在,则移动模式到该后缀的后面。

​3. 算法步骤​

  1. ​预处理​​:
    • 为模式生成坏字符表(记录每个字符最后出现的位置)。
    • 生成好后缀表(记录后缀的匹配情况)。
  2. ​匹配​​:
    • 从右向左比较字符,根据规则跳过尽可能多的位置。

​4. 示例分析​

​示例 1:坏字符主导​


​示例 3:综合应用​


​5. 算法复杂度​

  • ​文本(T)​​:ABCABCDAB
  • ​模式(P)​​:ABD
  • ​匹配过程​​:
    1. 对齐起始位置,从右向左比较:
      ABCABCDAB
      ABD
        ^ 不匹配(C vs D)

    2. ​坏字符规则​​:C不在模式中,模式右移3位:
      ABCABCDAB
         ABD

    3. 匹配成功(位置4)。

  • ​示例 2:好后缀主导​
  • ​文本(T)​​:ABABABAC
  • ​模式(P)​​:ABABAC
  • ​匹配过程​​:
    1. 对齐起始位置,从右向左比较:
      ABABABAC
      ABABAC
          ^ 不匹配(B vs C)

    2. ​好后缀规则​​:已匹配后缀ABA,在模式中查找其前缀ABA并对齐:
      ABABABAC
          ABABAC

    3. 匹配成功(位置3)。
  • ​文本(T)​​:GCTTCTGCTAC
  • ​模式(P)​​:TCTG
  • ​匹配过程​​:
    1. 初始对齐:
      GCTTCTGCTAC
      TCTG
       ^ 不匹配(C vs T)

    2. ​坏字符规则​​:C在模式中最后出现位置是第2位,右移1位:
      GCTTCTGCTAC
       TCTG

    3. 从右向左比较,完全匹配(位置3)。
  • ​最坏情况​​:O(n/m)(n为文本长度,m为模式长度)。
  • ​最佳情况​​:O(n/m)(如坏字符每次跳过整个模式长度)。

6.C++17 引入的 std::boyer_moore_searcher

如果允许使用 C++17,匹配字符串时候可以使用 std::boyer_moore_searcher(基于 Boyer-Moore 算法,比 KMP 更快):

#include<bits/stdc++.h>
using namespace std;

int main() {
    std::string text = "ABABDABACDABABCABAB";
    std::string pattern = "ABABCABAB";

    auto searcher = boyer_moore_searcher(pattern.begin(), pattern.end());
    auto it = search(text.begin(), text.end(), searcher);

    if (it != text.end()) {
        cout << "Pattern found at index: " << (it - text.begin()) << endl;
    } else {
        cout << "Pattern not found." << endl;
    }
    return 0;
}

7.使用 std::boyer_moore_searcher 找出所有匹配位置​

#include <bits/stdc++.h>
using namespace std;

vector<size_t> findAllMatches(const string &text, const string &pattern) {
    vector<size_t> matches;
    if (pattern.empty()) return matches;

    auto searcher = boyer_moore_searcher(pattern.begin(), pattern.end());
    auto it = text.begin();
    while (it != text.end()) {
        auto match = search(it, text.end(), searcher);
        if (match == text.end()) break; // 没有更多匹配

        size_t pos = match - text.begin();
        matches.push_back(pos);
        it = match + 1; // 继续搜索下一个可能的匹配
    }

    return matches;
}

int main() {
    string text = "ABABDABACDABABCABAB";
    string pattern = "ABAB";
    vector<size_t> matches = findAllMatches(text, pattern);

    for (size_t pos : matches) {
        cout << "Found at index: " << pos << endl;
    }
    return 0;
}

字符串匹配算法总结:​

方法时间复杂度适用场景
std::string::find()O(n*m)简单搜索,适用于短字符串
​KMP 算法​O(n+m)需要高效匹配时(手动实现)
std::boyer_moore_searcher (C++17)O(n/m)适用于长字符串,性能最好

附加:手动实现boyer_moore算法(非常复杂,容易出错,慎用) :

简化版:
Boyer-Moore 算法简介(简化版)
  • 核心思想:从右向左比较模式串(pattern)和文本(text);

  • 遇到不匹配:通过“坏字符规则”尽可能多跳;

  • 复杂度:最坏 O(nm),但在实践中非常快(接近 O(n));

  • 适用于:长文本 + 长模式串。

预处理步骤:构建坏字符表(last occurrence)

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>

using namespace std;

int boyerMooreSearch(const string& text, const string& pattern) {
    int n = text.size();
    int m = pattern.size();
    if (m == 0) return 0; // 空模式串匹配位置为0

    // 构建坏字符规则表:记录每个字符在模式串中的最后出现位置
    vector<int> badChar(256, -1); // 支持 ASCII 256 字符
    for (int i = 0; i < m; ++i) {
        badChar[(unsigned char)pattern[i]] = i;
    }

    int shift = 0; // 主串中当前匹配窗口的起点
    while (shift <= n - m) {
        int j = m - 1;

        // 从右向左比较
        while (j >= 0 && pattern[j] == text[shift + j]) {
            --j;
        }

        // 匹配成功
        if (j < 0) {
            return shift; // 找到匹配位置
        }

        // 否则根据坏字符规则移动
        char mismatchedChar = text[shift + j];
        int lastOccur = badChar[(unsigned char)mismatchedChar];
        int move = max(1, j - lastOccur); // 至少移动一位
        shift += move;
    }

    return -1; // 未找到
}
int main() {
    string text = "HERE IS A SIMPLE EXAMPLE";
    string pattern = "EXAMPLE";
    int pos = boyerMooreSearch(text, pattern);

    if (pos != -1) {
        cout << "Pattern found at position: " << pos << endl;
    } else {
        cout << "Pattern not found." << endl;
    }

    return 0;
}

输出:

Pattern found at position: 17

完整版:
完整版原理简述
  • 坏字符规则:失配时,将模式串对齐文本中下一个该字符的出现位置。

  • 好后缀规则:失配时,尝试对齐前面已匹配的后缀在模式串中其他位置的出现。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

class BoyerMoore {
public:
    BoyerMoore(const string& pattern) : pat(pattern), m(pattern.length()) {
        preprocessBadChar();
        preprocessGoodSuffix();
    }

    int search(const string& text) {
        int n = text.length();
        int s = 0; // s 是主串的滑动窗口起点

        while (s <= n - m) {
            int j = m - 1;

            // 从右向左匹配
            while (j >= 0 && pat[j] == text[s + j]) {
                --j;
            }

            if (j < 0) {
                return s; // 匹配成功
            } else {
                int badCharShift = j - badChar[(unsigned char)text[s + j]];
                int goodSuffixShift = goodSuffix[j + 1];
                s += max(badCharShift, goodSuffixShift);
            }
        }

        return -1; // 没找到
    }

private:
    string pat;
    int m;
    vector<int> badChar;    // 坏字符表
    vector<int> goodSuffix; // 好后缀表

    void preprocessBadChar() {
        badChar = vector<int>(256, -1); // 初始化为 -1
        for (int i = 0; i < m; ++i) {
            badChar[(unsigned char)pat[i]] = i;
        }
    }

    void preprocessGoodSuffix() {
        goodSuffix = vector<int>(m + 1, m); // 默认向后移动 m 位
        vector<int> suffix(m + 1, -1);      // suffix[i]: 长度为 i 的后缀的最右起点

        // 计算 suffix 数组
        int f = 0, g = m - 1;
        suffix[m - 1] = m - 1;
        for (int i = m - 2; i >= 0; --i) {
            if (i > g && suffix[i + m - 1 - f] < i - g) {
                suffix[i] = suffix[i + m - 1 - f];
            } else {
                if (i < g) g = i;
                f = i;
                while (g >= 0 && pat[g] == pat[g + m - 1 - f]) {
                    --g;
                }
                suffix[i] = f - g;
            }
        }

        // 构建 goodSuffix 表
        for (int i = 0; i < m; ++i) {
            goodSuffix[i] = m;
        }

        for (int i = m - 1; i >= 0; --i) {
            if (suffix[i] == i + 1) {
                for (int j = 0; j < m - 1 - i; ++j) {
                    if (goodSuffix[j] == m) {
                        goodSuffix[j] = m - 1 - i;
                    }
                }
            }
        }

        for (int i = 0; i < m - 1; ++i) {
            goodSuffix[m - 1 - suffix[i]] = m - 1 - i;
        }

        goodSuffix[m] = 1; // 避免越界
    }
};
int main() {
    string text = "abacaabaccabacabaabb";
    string pattern = "abacab";

    BoyerMoore bm(pattern);
    int pos = bm.search(text);

    if (pos != -1)
        cout << "Pattern found at position: " << pos << endl;
    else
        cout << "Pattern not found." << endl;

    return 0;
}

输出:

Pattern found at position: 10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值