(b2)蛮力匹配

官官方信息技术老师·14 页·深入(追求细节与边界)·0 次浏览·38 天前
串匹配算法基础边界分析

蛮力匹配

看完你能逐字符推演暴力匹配全过程,并说清它为何慢、何时够用

按 空格/→ 演示下一步

问一句
1 / 14 页

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

串匹配算法基础边界分析

蛮力匹配

看完你能逐字符推演暴力匹配全过程,并说清它为何慢、何时够用

1第 1 页 · 蛮力匹配

什么是串匹配

上一页我们把蛮力匹配的代码跑了一遍。在拆解它为什么慢之前,得先把问题本身说清楚:所谓串匹配,到底在主串里找什么、找到哪一步才算找到?

主串与模式串
主串是被搜索的长文本,模式串是要找的那一小段
对齐位置
模式串左端对准主串第 i 位,称为一次尝试
匹配判定
逐字符比较,全部相等则匹配成功,中途失配则右移一位
所有可能位置
共 n−m+1 个对齐点,蛮力法逐一尝试
拿钥匙试锁孔对应 →对齐尝试

钥匙对准锁孔是对齐位置,能否拧动是匹配判定

2第 2 页 · 什么是串匹配

蛮力法的构思

前页说了:串匹配就是在长文里找短词的位置。那最直接的办法是什么?把短词按在长文的开头,一位一位比对,不行就换个位置再按一次——这就是蛮力法。

逐位对齐
模式串从主串某起点开始,逐字符比对
失配即移位
一旦有字符不等,本轮宣告失败
右移重来
起点右移一位,回到第一个字符重新比
用钥匙试锁对应 →蛮力匹配

钥匙插进锁孔逐齿比对,齿形不对就拔出来换一把再插

3第 3 页 · 蛮力法的构思

对齐与移动的全景

图示主串、模式串、对齐位置的关系

对齐与移动的全景
图示主串、模式串、对齐位置的关系
4第 4 页 · 对齐与移动的全景

版本一:从右向左比对

在每个对齐位置,从模式串末字符开始、从右向左逐个比对。

1
对齐起点
把模式串左端对齐文本当前位置,末字符上下对齐
2
比末字符
先比最右侧字符:不等就说明此位置整体失配
3
右往左推进
末字符相等才继续,指针左移一位比前一个字符
4
全中即命中
从末字符一路比到首字符都相等,匹配成功
5第 5 页 · 版本一:从右向左比对

版本一代码

python

版本一的完整实现:把上一页的比对逻辑落成可运行代码,逐行看清从右向左的策略。

代码高亮加载中…

第 5 行确定最多 n-m+1 次对齐;第 7 行把指针从 m-1 起从右向左走;第 9 行是失败短路——一旦失配立刻停止本轮;第 13 行验证真实输出。

6第 6 页 · 版本一代码

版本二:从左向右比对

换一种思路:对齐后从最左字符开始比,一旦失配立即停下。

1
左端对齐
模式串与文本串左端对齐,准备从首位开始逐字符比较
2
从左逐字比
比较首字符,相同则继续向右推进一位
3
失配即停
遇到第一个不匹配的字符就立刻停止,不再比对剩余部分
4
整体右移
对齐位置右移一格,回到第2步重新从最左字符开始比较
7第 7 页 · 版本二:从左向右比对

版本二代码

python

从左向右比对的蛮力匹配实现:逐行读循环与边界,看清移位逻辑藏在哪一行为。

代码高亮加载中…

range(n-m+1) 保证最后 m 个字符也对齐;T[i+j] 同时表达「对齐 i」与「从左向右推进 j」;j==m 才是匹配成功,而非相等本身。

8第 8 页 · 版本二代码

版本一 vs 版本二

两个版本只是比对方向相反,细节差异却常被忽略——它们是否真的等价?

版本一:右→左
  • 从模式串末字符开始比对
  • 主串指针可整体回退,最坏 O(mn)
  • 坏消息来得晚,发现失配前匹配了更多字符
版本二:左→右
  • 从模式串首字符开始比对
  • 同样逐窗口移动,最坏仍 O(mn)
  • 坏消息来得早,首个字符即失配可快速换位
正确的理解是:两版本渐进复杂度相同、结果等价,因为方向只改变失配时机,不改变对齐与移动策略。
9第 9 页 · 版本一 vs 版本二

最好情况

前面看到版本一比版本二平均更快。但真正的差距在极端情况下更悬殊:有时每次比对只要一眼就能断定失败,有时一次对齐就全部命中——这时两版都退化为最省事的线性扫描。

首对齐即成功
模式串开头就对上,n 次比对后直接匹配成功
首字符即失败
每次对齐的首字符就不同,比对 1 次即右移
移动次数为 O(n)
n 个对齐位置 × 每处 O(1) 比对,共 O(n+m)
逐个翻牌对应 →首字符即失败

翻一张就发现不对,直接翻下一张,不必翻完整副

T(n)=O(n)⋅O(m)=O(n+m)≈O(n)T(n)=O(n)\cdot O(m)=O(n+m)\approx O(n)
10第 10 页 · 最好情况

最坏情况

上一页看到最好情况一次就中,但运气不会天天好。最倒霉的时刻是:每一轮都对到最后一个字符才宣告失败,前面 m-1 次比对全白费。

何时最坏
主串如 aaa…ab,模式串 aaab,每次仅末字符失配
单轮代价
每轮比对 m 个字符才失败,无法提前退场
轮数不减
失败后仅右移一格,约 n-m+1 轮
总代价
轮数乘每轮代价,得 O(n·m)
翻字典找词对应 →蛮力匹配最坏情况

每次翻到最后一个字母才发现不对,只能翻下一页重来

T(n,m)=O((n−m+1)⋅m)=O(n⋅m)T(n,m)=O\big((n-m+1)\cdot m\big)=O(n\cdot m)
11第 11 页 · 最坏情况

自测

点击作答

蛮力匹配中,一次比对失败后,模式串相对文本串移动多少位?

12第 12 页 · 自测

小结

  • ✓匹配失败时的移动量决定整体效率
  • ✓比对方向影响的是常数,不改变 O(mn) 上界
  • ✓最好与最坏情况差距可达线性 vs 平方
  • ✓正确但慢,正是它成为一切改进的基线
  • ✓优化方向:失败后利用已比对的信息多移几步
延伸主题:KMP:失败函数与不回退匹配Boyer-Moore:坏字符与好后缀Rabin-Karp:滚动哈希加速
13第 13 页 · 小结

课后思考

先自己想几分钟,再对照参考答案——开放问题没有唯一解。

1蛮力法最坏 O(nm) 的根源是什么?是比对方向,还是别的?

参考答案从「每次失配只挪一格、不做任何记忆」入手:方向只是表象,本质是失败后信息全丢,主串指针回退,导致重复比对。

2文本编辑器的查找功能为什么大多仍用蛮力思路?

参考答案实际场景中模式短、字母表大、失配早、随机数据期望接近 O(n+m),实现简单缓存友好;工程上是「够用且稳」,值得从成本收益角度想。

3如果把字符集从 26 个字母换成 {0,1},蛮力法性能会怎样变化?

参考答案字母表越小,随机失配越晚、伪命中越多,期望性能越差;这提示复杂度不只看 n、m,还和字符集大小 σ 有关。

14第 14 页 · 课后思考