码迷,mamicode.com
首页 > 编程语言 > 详细

KMP算法详解

时间:2017-06-22 00:18:01      阅读:159      评论:0      收藏:0      [点我收藏+]

标签:对齐   log   语言   下标   主程   指针   strstr   第3版   c++   

本文的是基于我对邓俊辉老师编著《数据结构(C++语言版)(第3版)》上关于KMP算法的理解,和网络上一些大神们写的博客,所写。建议将我写的关于implement strstr这题的博客和本篇连起来读。

不难发现,这里存在大量的局部匹配,针对暴力解法,若每次匹配的过程都是最后一位失配(即不匹配),文本串和模式串都得回退,并从头开始下一轮的尝试,这样浪费很多时间。

1)我们可以利用以往的成功对比所提供的信息,不仅可避免文本串字符指针的回退,而且可使模式串尽可能大跨度的地右移。我们举一个例子来得出KMP的思考过程;

技术分享

本轮发现:T[i]=‘E’ !=‘O’=P[4]失配以后,在保持i不变的同时,应将模式串P右移几个单位?观察得出T[i-1,i) = P[0,4) = ‘REGR‘。也就是说,若比较到了T[i]说明,不管T[i]是否匹配,至少T[i]左侧的若干字符均应匹配,P[0]和T[i-1]就是这种情况。若注意到 i-1是能够如此匹配的最左侧,即可直接将P右移4-1=3个单位(等效于i保持不安,同时令 j=1),然后继续对比。

2)如图,若上例中,对比终止于T[i] !=P[j],按以上构想,指针 i 不必回退,而将T[i]与P[t] 对齐开始下一轮对比,那么关键是:t准确应取做多少?

技术分享

 

在上一轮的对比中可知,P[0,j) =T[i-j,i) ,若模式串P经过适当的右移移,以后,能过与T的某一(包含T[i]在内的)子串完全匹配,则必定满足的一项必要条件是:P[0,t)=T[i-t,j)=p[j-t,j),亦即,在P[0,j)中长度为t的真前缀应与长度为t的真后缀完全匹配,故t必来自集合:

N(P,j) ={ 0<=t<j | P[0, t) = P[j-t, j) }                      .......(1)

一般,该集合中,可能包含多个这样的t ,但值得注意的是:t 值的构成,仅取决于模式串P以及前一轮对比的首个失配位置P[j] ,而与文本串T无关。若下一轮从 T[i] 与 P[t] 对比开始,等效于将P右移了 j-t 个单元,与 t 成反比。为保证指针 i 绝不退后,同时又不遗失可能的匹配,应保证,选取最大的 t 值,保证,移动的距离最短。这里很关键的是如何选取 t 值,即next[ ]的构造。

3)只要 j >0,即失配时,模式串P的下标大于0,则必有0属于集合N(P ,j),也就是说,针对表达式(1)中的集合非空,最坏的情况是 t=0,即模式串又重新的从开始和文本串T[i]开始对比,相当于向右移动 j 个单位。下表的意思是,当失配的位置rank的值等于0~9时,模式串下轮对比从P的那个位置开始。如:rank=3时失配,因为,只有当 t =0 时,才满足表达式(1),即P[0,3)中真前缀没有和真后缀匹配的情况,则下轮比较还是要从模式串的首元素开始比较,所以next[3] =0,即向右移动3个单位;当rank=6时,P[0,2) =P[4,6) ,所以下一轮的对比中,应该是 P[2]和T[i],做对比,而对模式串P而言相当于向右移了6-2=4个单位。如果某一轮的对比中,首字符就失配,此时应该进行时,用模式串的首字符和文本串T的下一字符做比较,为统一程序和达到依旧用首字符和文本串中的下一位做比较的目的,可以令next[0]=-1,这样,i 和 j都++以后,依旧是符合要求的。故在主程序中,i++和 j++的条件是 if(0>j || T[i]==P[j]) 。

技术分享

 3.1)经过3)我们得到了next[j] ,但是如何计算出next[j+1]了?

若next[j] = t ,则意味着在P[0,j)中,自匹配的真前缀和真后缀的最大长度为t ,故必有 next[j+1] <=next[j]+1 ,当且仅当P[j] = P[t]时,取等号。等号怎么理解了?如图:

技术分享

针对模式串P中的 j 点,满足P[0,t)=P[j-t,j),则当P[t] =P[j] 时,对模式串P的 j+1 点而言,应该是P[0,t+1) =P[j-t, j+1) ,而P[0,t+1)等于next[j]+1。至于为什么事最大值,这点比较好想,这里接不解释了。

那么一般的情况,若P[j] !=P[t]时,又该如何的得到next[j+1] ?这点可以想到,若是从next[j+1]开始比较,说明,P中j点而言都是满足P[0,t ) =P[j-t),同时也肯定存在P[t]=P[j]。由next表的功能定义可知,next[j+1]从next[next[j]+1 ]+1,next[ next [next[j] ] ]+1..........中按照优先次序遍历寻找P[j] =P[t] ,即,反复用next[t] 替代 t(令 t=next[t] ) ,从而可令next[j+1] = next[t]+1 。具体的程序过程参考文章开始说的博客。

KMP算法详解

标签:对齐   log   语言   下标   主程   指针   strstr   第3版   c++   

原文地址:http://www.cnblogs.com/love-yh/p/7062068.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!