(e1)BM_GS算法:好后缀

官官方信息技术老师·13 页·深入(追求细节与边界)·0 次浏览·38 天前
字符串匹配BM算法好后缀图解推导

BM 算法的好后缀策略

看完你能亲手推导好后缀移位表并解释每种失配情形

按 空格/→ 演示下一步

问一句
1 / 13 页

全部页面点击任意一页,跳回舞台从这页播放

字符串匹配BM算法好后缀图解推导

BM 算法的好后缀策略

看完你能亲手推导好后缀移位表并解释每种失配情形

1第 1 页 · BM 算法的好后缀策略

什么是「好后缀」

上一页说,BM 算法遇到不匹配时不是一格一格退,而是聪明地跳。可跳之前得先看清现场:模式串尾部总有一段和文本对上了,这段「已拼好」的部分就是突破口——它叫好后缀。

位置在尾部
好后缀是模式串末尾与文本已匹配的那段,紧跟在坏字符左边
完整匹配
注意:是模式串自身的尾部片段与文本对齐,不只是文本侧的对上了
跳动的依据
失配时,算法拿好后缀在模式串里找它能再次出现的位置,决定滑多远
拼图对应 →好后缀

已拼好的那几块对应已匹配的后缀;拼不下去时,找别处能接上相同形状的位置

2第 2 页 · 什么是「好后缀」

好后缀引发的失配场景

从右向左比对时一旦失配,已匹配的尾部就是好后缀,下方分支展示三种滑动决策路径。

图解渲染中…
c1:失配位置右侧那段已成功匹配的子串g1:好后缀在模式中再无出现,可直接滑过它h1:好后缀无再现,但模式前缀与其尾部重合时对齐前缀
3第 3 页 · 好后缀引发的失配场景

情形一:好后缀完整再现

当最长好后缀在模式串别处完整出现时,滑动到那个出现处对齐即可。

1
找出好后缀
取失配前已匹配的末段,如模式 ABABA 中失配于末位,好后缀是 ABA
2
向左搜索
在模式串更左位置查找与好后缀完全相同的子串,且其前一个字符不等于失配字符
3
对齐并滑动
把模式串滑到该完整出现处与文本对齐,跳过中间不可能成功的位置
4第 4 页 · 情形一:好后缀完整再现

情形二:部分前缀匹配

好后缀在模式串里找不到完整再现时,退一步看它的子串是否匹配模式串前缀。

1
完整查找失败
好后缀在模式串其余部分无完整出现,进入情形二
2
找最长子串
取好后缀的最长真子串,检查它是否等于模式串前缀
3
前缀对齐
把模式串左端对齐到该匹配子串的位置,跳过中间无效对齐
5第 5 页 · 情形二:部分前缀匹配

情形三:干脆滑过去

三种情形都不满足时,只能把整个好后缀滑过去,找新的对齐起点。

1
逐一检查失败
好后缀找不到完整再现,也没有任何前缀能在文本中命中
2
滑过整个m
好后缀长 m,直接右移 m 位,跳到失配点之后重新对齐
3
从头比较
新起点没有可复用的信息,模式串从首字符重新比对
6第 6 页 · 情形三:干脆滑过去

三种情形取最长

三种情形各有移动表,失配时到底听谁的?这是好后缀策略最易混淆的一步。

逐条理解各情形
  • 情形一:整个好后缀再次完整出现,滑到它对齐
  • 情形二:只有前缀匹配,滑到前缀对齐
  • 情形三:哪儿都不出现,直接滑过整个模式串
实际执行取最大
  • 三表同时可查,每步都要三选一
  • 三个候选距离各算出来
  • 取最大者移动,绝不可能滑过头
正确的理解是:三种情形互不排斥,各给一个安全距离,取最大值即可,因为最远的那个也保证不会错过匹配。
7第 7 页 · 三种情形取最长

实例体验:一步步滑动

以模式串 ABCAB 为例,完整走一遍好后缀驱动的滑动过程。

1
右端对齐
把 ABCAB 的末字符对准文本窗口末位,从右往左比较
2
尾部匹配
末尾两位 AB 逐字符相等,AB 成为当前好后缀
3
遭遇失配
比较到第三位 C 时文本字符不同,匹配中断
4
查好后缀
AB 在模式串内不再完整出现,退而求其次查前缀
5
前缀对齐
开头前缀 AB 恰与好后缀 AB 相同,可安全对齐
6
一次滑 3 格
模式串整体右移 3 位,前缀 AB 落在原好后缀位置
8第 8 页 · 实例体验:一步步滑动

坏字符 vs 好后缀

两者都是 BM 的滑动依据,初学者常分不清失配时该看谁、听谁的。其实失配处右侧是好后缀、失配处本身是坏字符。

坏字符策略
  • 看失配处的文本字符
  • 模式右移对齐该字符
  • 无此字符可整段滑过
好后缀策略
  • 看失配处右侧已匹配段
  • 右移找好后缀再次出现
  • 找不到就退而求前缀
正确的理解是:两条规则并行计算、各算一个移动量,每次取更大者,而非二选一。
9第 9 页 · 坏字符 vs 好后缀

gs 表的构造思路

python

看看好后缀滑动表怎么预处理:从右往左匹配前后缀,三情形一次算完。

代码高亮加载中…

gs 数组先把情形二的前缀匹配位移算好,剩下没赋值的位置统一填 m,即情形三「干脆滑过去」。

10第 10 页 · gs 表的构造思路

自测:滑多远

点击作答

BM 好后缀失配后,文本串指针该如何移动?

11第 11 页 · 自测:滑多远

要点收束

  • ✓好后缀是失配处右侧已匹配的子串
  • ✓滑动距离取决于好后缀在别处的再现
  • ✓三种情形取最长滑距,绝不回退
  • ✓gs 表预计算,查询 O(1)
  • ✓与坏字符策略取更大者,整体 O(n/m)
延伸主题:gs 表的线性时间构造证明Galil 规则与线性时间保证BM 与 KMP 的实测性能对比
12第 12 页 · 要点收束

课后思考

先合上笔记自己想,再对照参考答案,看看思路差在哪。

1好后缀的三种情形为什么必须取滑动距离最短的那个?取最大会发生什么?

参考答案取最短是为了不跳过任何可能的匹配位置。三种情形给出的是不同可信度的对齐点,取最远可能越过某个恰好匹配的起点,导致漏解,正确性就没了。

2如果文本里坏字符频繁出现且模式很短,坏字符和好后缀策略谁更占优势?

参考答案短模式下好后缀往往退化:m 小,子串在模式里再现的概率低,多数落在情形三。坏字符不受模式长度影响,短模式时通常靠坏字符拉动更多,这正是实践中两者结合的原因。

3把好后缀的「后缀重复」思想搬到字符串匹配之外,还能用在什么场景?

参考答案凡是「失败后利用已确认信息定位下一步」的场景都类似:比如 KMP 的失败函数本质也是利用已匹配前缀;编辑去重、日志压缩里利用重复片段跳过比对,都是同一思路的迁移。

13第 13 页 · 课后思考