仅用 bad_char 规则即可满足多数实际场景需求;需预填-1并记录字符最右出现位置,强制 static_cast 防负下标,shift 至少为1。

只用 bad_char 规则就足够快
绝大多数实际场景(日志关键词扫描、配置项查找、源码关键字定位)根本用不上 good_suffix 表。手写完整 BM 反而容易在边界条件上出错,比如 j 下溢、i 越界、bad_char 查表时传入负值 char。C++17 用 std::array<int></int> 预填 -1,再遍历模式串更新最右位置,就能覆盖 ASCII 范围全部可能字符,简洁且无符号扩展风险。
bad_char 表必须记录“最右出现位置”,不是首次
这是最容易写反的点:如果把 pat[i] 第一次出现的位置记进表,失配时算出的跳跃步数会偏小,导致漏匹配。例如模式串 "ABAC",字符 'A' 出现在索引 0 和 2,表里必须存 2,否则遇到文本中 'A' 时会少跳一位。
实操建议:
- 初始化
bad_char.fill(-1),避免未初始化值干扰 - 循环用
bad_char[static_cast<unsigned char>(pat[i])] = i</unsigned>,强制转成unsigned char防止char是 signed 时下标为负 - 查表时同样要
static_cast<unsigned char>(txt[i + j])</unsigned>,否则txt里含\xFF这类字节会崩
失配后跳跃距离不能小于 1
公式 shift = j - bad_char[...] 可能算出 0 或负数(比如坏字符在模式串中出现在 j 右侧,或 bad_char 值是 -1)。直接加这个值会导致 i 停滞甚至倒退,死循环。
必须用 std::max(1, shift) 截断。注意不是 std::max(0, shift) —— 移动 0 步毫无意义,至少得挪 1 位。
主循环的边界检查别写成 i
常见错误是用 i 作 while 条件,但 <code>n 和 m 是 size_t,一旦 n 就变成极大正数,循环失控。正确做法是显式转成有符号整型(如 <code>int n = txt.size()),再用 i 。
另外,匹配成功后不要盲目加 m 继续搜——那会跳过重叠匹配。如果需求是找所有位置,应只加 1;如果明确不需要重叠(如分词),才可加 m。
真正卡住人的从来不是算法原理,而是 static_cast<unsigned char></unsigned> 漏写、std::max(1, ...) 忘包、size_t 混合运算这些细节。写完立刻拿 ""、"a"、"aa" 和越界文本跑一遍,比看十遍图解都管用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











