随笔-5  评论-24  文章-0  trackbacks-0
Knuth大神酷爱将算法的效率推向极致。今天看在网上看到一个牛人分析KMP算法,写的蛮好,总结一下。
我们知道普通的模式匹配算法,最差情况下复杂度是O(m*n)的(其中n为母串长度,m为子串长度),因为需要逐一按位匹配。而KMP算法的优越处在于它利用了如下事实:
白色区域为母串A
彩色区域为子串B
我们假设图中子串的淡蓝色区域的数列与母串匹配,下一项(图中灰色区域)与母串当中的下一项不匹配。一般情况下,我们会让母串指针回到原位置然后加一,然后寻求新的可能最大匹配。
好,我们假设上述过程进行顺利,我们找到了第一处长度不小于蓝色区域长度的序列与子串匹配(因为只有在长度不小于才有意义,即可能完成匹配)。现在图中实线双箭头的区域必然与子串虚线双箭头区域匹配。而在上次匹配中,母串与子串的实现双箭头区域是匹配的。
这说明了什么?——子串前后必然存在相等的序列!倘若我们能利用这一事实,则不用让母串指针回到原位置,而是move on.这样就大大简少“不可能完成任务”的比较次数,提高效率,实现优化。
自然,我们就会问,那岂不是要知道子串任意位置的最大前后匹配长度?
对,为了能在遇到不匹配项的时候能有效找到新的起始匹配位置,我们需要事先记录,自此假设用数列P[]记录。但如何高效的找到任意位置的这个值呢?
我们来观察一下可能的情形:
若前一次能够匹配的子串序列为:abab , 显然P[1] = 0; P[2] = 0; P[3] = 1 ; P[4] = 2;
现假设序列的第五位为a,那么P[5]等于多少呢?由于P[4] = 2 (意味着前面刚好可以划分为相等的两端),而B[5](数列以1开始记录) = B[1];所以P[5] = P[4] + 1;
若序列的第五位为b,那我们要怎么找寻P[5]? 既然B[ P[4]+1 ]<>B[5](意味着新加入的项将导致前后匹配的变短,但至少为0,因为单个数据项a的P[1] = 0),那么我们只能看B[P[P[4]]+1]是否等于B[5].并以此递归进行下去。结果没有一个满足,所以P[5] = 0.
上述过程是一个递归寻找最大匹配的过程。举一个特殊的例子:
已找到abcdeeabcd abcdeeabcd,显然当前最大前后匹配项为abcdeeabcd 若现在加入的为e,显然根据上述递归过程结果最大的前后匹配项伟abcde,满足条件。
小小的总结一下:巧妙处在于注意到存在前后相等的事实以及新的查找能够成功的匹配必然比前一次未成功的匹配长度长。
由于只需要对B进行预处理,所以在给定一个B和一群不同A的情况下,该算法将大大减少执行效率。该算法时间复杂度为O(n)
具体分析过程,将用到时间复杂度的摊还分析中的主要策略,简单地说就是通过观察某一个变量或函数值的变化来对零散的、杂乱的、不规则的执行次数进行累计。可以参考给出的链接地址。
原文地址为:http://www.matrix67.com/blog/archives/115
posted on 2010-11-19 01:04 yenchieh 阅读(2247) 评论(0)  编辑 收藏 引用 所属分类: Datastructure and Algorithm

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理