当前课程知识点:数据结构(下) > 第十一章 串(上) > (c6)KMP算法:再改进 > 11c6-5 : 可视对比
在告别KMP算法之前
我们不妨以一种可视的形式
将其与蛮力算法作一对比
我们此前曾经指出
对于串匹配算法的评判与对比
需要分成功和失败两种情况分别进行
因此这里 我们不妨只针对失败的情况
对二者的最好与最坏性能作一对比
在这个图中 我们用这条线来表示文本串
它相对而言更长
而模式串的长度m则相对更小
在蛮力算法中 如果我们将文本串固定于此
然后将模式串在各次迭代中的快照记录下来
并按照与文本串相应的对齐位置 如此排列
就会得到这样一个相对比较窄
同时非常高的平行四边形
当然 既然是失败情况 所以 每一轮的比对过程都类似
具体来说 都是在经过了一系列的成功比对之后
最终失败于某个位置
而一旦失败之后 算法都会将模式串右移一个字符 并重新开始一轮比对
而且同样地 接下来也会经过一系列的成功比对 并最终失败于某个位置
这样的故事会反复地发生
直到最终模式串试图越过文本串的右侧边界
从这个图中 我们可以清晰地看到
在每一个对齐位置所消耗的计算成本
都可相应地由深色区间的宽度来度量
整个算法累计所消耗的时间
应该也就是这些深色区间的宽度总和
那么这一总和的变化范围是多大呢
首先我们注意到有最坏情况
也就是在某一个对齐位置
可能会持续地经过m-1次成功的比对
而最终失败于第m次比对
而整体的最坏情况
莫过于每次都出现这样的最坏情况
关于这一点 我们在此前曾经举过一个具体的实例
既然在数量级上 m要小于n
所以这个平行四边形的高度 也就可以直接度量为n
因此 平行四边形的面积 也自然就是n*m
这也是蛮力算法在最坏情况下的性能
再来看看KMP算法
我们同样地可以将所有潜在的和实在的对齐位置
按照刚才的方式排列起来
从而同样得到这样一个平行四边形
然而幸运的是
因为KMP算法的智能性
它所消耗的时间
要远远地小于这个平行四边形的面积
来看右侧这个局部放大图
在这里我们可以清晰地看到KMP的两种优化效果
你应该记得 KMP首先能够令模式串得以快速地滑动
也就是说 它有可能会直接越过一些潜在的对齐位置
在每一次快速的滑动之后
它也不必继续从头开始比较
而事实上 它只需从刚才失败的那个位置出发
继续开始下一轮的比对
如果有必要 在经过下一次的快速移动之后
它依然会从新的失败位置开始
启动下一轮的比对
以致再下一轮
以及再再下一轮比对
同样地 在这样的一个计算过程中
我们所花费的时间也可以用这些深色的区间来表示
它们的宽度之和
也就给出了KMP算法总体的计算时间
为了统计出所有这些深色区间的宽度之和
我们不妨回到左侧这幅总览图
不难看出 如果将所有这些深色的区间 都沿着垂直方向投影到文本串上
我们就会发现 任何两段深色的区间
除了在端点处之外 绝不会有任何的重叠
因此 既然文本串的长度为n
我们也可以由这个图直接推知
整个KMP算法所消耗的时间总量
也不会超过2n
这与我们此前的分析完全一致
在学习过KMP算法之后
如果我们回过头来再看看蛮力算法
你或许会对它不屑一顾
是的 就最坏情况下的效率而言
这个算法的确不值一提
然而在此 我们也不妨为蛮力算法作些辩护
如果还不是平反的话
事实上 即使是在失败情况下
蛮力算法也有最好的情形
你能想到吗
是的 在总体失败的情况下
蛮力算法依然需要在每一个对齐位置作一轮比对
然而在最好的情况下
每一步迭代 可能只牵涉到常数 甚至只有一次比对
是的 只经过一次比对
就可排除掉一个对齐的位置
你应该不难构造出这样的实例
因此在这种情况下
为了排除掉所有的O(n)个对齐位置
蛮力算法所消耗的时间 累计也不过是线性的
当然 你有可能会质疑这种最好情形所发生的概率
事实情况是 在通常的情况下 这个概率并不像你想象的那么低
而且 随着字符集规模的增大
这个概率也会急速地提高
也就是说 在这类情况下
KMP算法相对于蛮力算法的优势 也就不那么明显了
或者反过来等效地
只有在字符集规模相对很小时
KMP在性能上的优势
才能充分得以展示
你或许已经注意到了
在介绍KMP算法的诸多教材中
绝大多数的实例都是基于二进制串
如果你能够领会到我们刚才所说的那个现象
你就应该能够更好地理解那些作者的良苦用心了
-选课之前
--写在选课之前
--宣传片
-考核方式
--考核方式
-OJ系统说明
--关于OJ
--1-注册与登录
--2-界面与选课
--3-提交测试
-关于课程教材与讲义
--课程教材与讲义
-关于讨论区
--关于讨论区
-微信平台
--html
-PA晋级申请
--PA晋级
-(a)概述
--07A-1 纵览
--07A-5 接口
-(a)概述--作业
-(b1)BST:查找
-第七章 二叉搜索树--(b1)BST:查找
-(b2)BST:插入
-(b2)BST:插入--作业
-(b3)BST:删除
-第七章 二叉搜索树--(b3)BST:删除
-(c)平衡与等价
-(c)平衡与等价--作业
-(d1)AVL树:重平衡
-第七章 二叉搜索树--(d1)AVL树:重平衡
-(d2)AVL树:插入
-(d2)AVL树:插入--作业
-(d3)AVL树:删除
-(d3)AVL树:删除--作业
-(d4)AVL树:(3+4)-重构
-(d4)AVL树:(3+4)-重构--作业
-本章测验
--章节测验
-(a1)伸展树:逐层伸展
--习题
-(a2)伸展树:双层伸展
--习题
-(a3)伸展树:算法实现
--习题
-(b1)B-树:动机
--习题
-(b2)B-树:结构
--习题
-(b3)B-树:查找
--习题
-(b4)B-树: 插入
--习题
-(b5)B-树: 删除
--习题
-(xa1)红黑树:动机
--习题
-(xa2)红黑树:结构
--习题
-(xa3)红黑树:插入
--习题
-(xa4)红黑树:删除
-本章测验
--习题
-(b)散列:原理
--09B-3 数组
--09B-4 原理
--09B-5 散列
--09B-6 冲突
--习题
-(c)散列:散列函数
--习题
-(d1)散列:排解冲突(1)
--习题
-(d2)散列:排解冲突(2)
--习题
-(e)桶/计数排序
--习题
-本章测验
--本章测试
-(a1)需求与动机
--习题
-(a2)基本实现
--习题
-(b1)完全二叉堆:结构
--习题
-(b2)完全二叉堆:插入与上滤
--习题
-(b3)完全二叉堆:删除与下滤
--习题
-(b4)完全二叉堆:批量建堆
--习题
-(c)堆排序
--习题
-(xa1)左式堆:结构
--习题
-(xa2)左式堆:合并
--习题
-(xa3)左式堆:插入与删除
-本章测验
--本章测试
-(a)ADT
--习题
-(b1)串匹配
--习题
-(b2)蛮力匹配
--习题
-(c1)KMP算法:从记忆力到预知力
--习题
-(c2)KMP算法:查询表
--习题
-(c3)KMP算法:理解next[]表
--习题
-(c4)KMP算法:构造next[]表
--习题
-(c5)KMP算法:分摊分析
--习题
-(c6)KMP算法:再改进
-(d1)BM_BC算法:以终为始
-(d2)BM_BC算法:坏字符
-(d3)BM_BC算法:构造bc[]
-(d4)BM_BC算法:性能分析
-(e1)BM_GS算法:好后缀
-(e2)BM_GS算法:构造gs表
-(e3)BM_GS算法:综合性能
-(f1)Karp-Rabin算法:串即是数
-(f2)Karp-Rabin算法:散列
-本章测验
--本章测试
-(a1)快速排序:算法A
-- 12a1-5: 实例
--习题
-(a2)快速排序:性能分析
--习题
-(a4)快速排序:变种
-(b1)选取:众数
-(b3)选取:通用算法
--习题
-(c1) 希尔排序:Shell序列
--习题
-(c2)希尔排序:逆序对
-本章测验
--本章测试