ImageVerifierCode 换一换
格式:DOCX , 页数:37 ,大小:721.61KB ,
资源ID:9520803      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/9520803.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(kmp算法详解.docx)为本站会员(b****7)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

kmp算法详解.docx

1、kmp算法详解引记 此前一天,一位MS的朋友邀我一起去与他讨论快速排序,红黑树,字典树,B树、后缀树,包括KMP算法,唯独在讲解KMP算法的时候,言语磕磕碰碰,我想,原因有二:1、博客内的东西不常回顾,忘了不少;2、便是我对KMP算法的理解还不够彻底,自不用说讲解自如,运用自如了。所以,特再写本篇文章。由于此前,个人已经写过关于KMP算法的两篇文章,所以,本文名为:KMP算法之总结篇。 本文分为如下六个部分:1.第一部分、再次回顾普通的BF算法与KMP算法各自的时间复杂度,并两相对照各自的匹配原理;2.第二部分、通过我此前第二篇文章的引用,用图从头到尾详细阐述KMP算法中的next数组求法,并

2、运用求得的next数组写出KMP算法的源码;3.第三部分、KMP算法的两种实现,代码实现一是根据本人关于KMP算法的第二篇文章所写,代码实现二是根据本人的关于KMP算法的第一篇文章所写;4.第四部分、测试,分别对第三部分的两种实现中next数组的求法进行测试,挖掘其区别之所在;5.第五部分、KMP完整准确源码,给出KMP算法的准确的完整源码;6.第六步份、一眼看出字符串的next数组各值,通过几个例子,让读者能根据字符串本身一眼判断出其next数组各值。 力求让此文彻底让读者洞穿此KMP算法,所有原理,来龙去脉,让读者搞个通通透透(注意,本文中第二部分及第三部分的代码实现一的字符串下标i 从0

3、开始计算,其它部分如第三部分的代码实现二,第五部分,和第六部分的字符串下标i 皆是从1开始的)。 在看本文之前,你心中如若对前缀和后缀这个两个概念有自己的理解,便最好了。有些东西比如此KMP算法需要我们反复思考,反复求解才行。个人写的关于KMP算法的第二篇文章为:六(续)、从KMP算法一步一步谈到BM算法;第一篇为:六、教你初步了解KMP算法、updated(文末链接)。ok,若有任何问题,恳请不吝指正。多谢。第一部分、KMP算法初解1、普通字符串匹配BF算法与KMP算法的时间复杂度比较 KMP算法是一种线性时间复杂的字符串匹配算法,它是对BF算法(Brute-Force,最基本的字符串匹配算

4、法的)改进。对于给的原始串S和模式串P,需要从字符串S中找到字符串P出现的位置的索引。BF算法的时间复杂度O(strlen(S) * strlen(T),空间复杂度O(1)。KMP算法的时间复杂度O(strlen(S) + strlen(T),空间复杂度O(strlen(T)。2、BF算法与KMP算法的区别 假设现在S串匹配到i位置,T串匹配到j位置。那么总的来说,两种算法的主要区别在于失配的情况下,对的值做的处理: BF算法中,如果当前字符匹配成功,即si+j = Tj,令j+,继续匹配下一个字符;如果失配,即Si + j != Tj,需要让i+,并且j= 0,即每次匹配失败的情况下,模式串

5、T相对于原始串S向右移动了一位。 而KMP算法中,如果当前字符匹配成功,即Si=Tj,令i+,j+,继续匹配下一个字符;如果匹配失败,即Si != Tj,需要保持i不变,并且让j = nextj,这里nextj =1), 同时移动之后,i之前的部分(即Si-j+1 i-1),和j=nextj之前的部分(即T0 j-2)仍然相等。显然,相对于BF算法来说,KMP移动更多的位数,起到了一个加速的作用!(失配的特殊情形,令j=nextj导致j=0的时候,需要将i +,否则此时没有移动模式串)。3、BF算法为什么要回溯首先说一下为什么BF算法要回溯。如下两字符串匹配(恰如上面所述:BF算法中,如果当前

6、字符匹配成功,即si+j = Tj,令j+,继续匹配下一个字符): i+j(j随T中的j+变,而动)S:aaaacefghij j+T:aaac如果不回溯的话就是从下一位开始比起:aaaacefghijaaac看到上面红颜色的没,如果不回溯的话,那么从a的下一位c比起。然而下述这种情况就漏了(正确的做法当然是要回溯:如果失配,即Si + j != Tj,需要让i+,并且j= 0):aaaacefghijaaac 所以,BF算法要回溯,其代码如下:view plain1.intIndex(SStringS,SStringT,intpos)2./返回T在S中第pos个字符之后的位置3.i=pos;

7、j=1;k=0;4.while(i=S0&jT0)returni;/子串结束,说明匹配成功9.elsereturn0;10./Index 不过,也有特殊情况可以不回溯,如下:abcdefghij(主串)abcdefg(模式串) 即(模式串)没有相同的才不需要回溯。4、KMP算法思想 普通的字符串匹配算法必须要回溯。但回溯就影响了效率,回溯是由T串本身的性质决定的,是因为T串本身有前后部分匹配的性质。像上面所说如果主串为abcdef这样的,大没有回溯的必要。 改进的地方也就是这里,我们从T串本身出发,事先就找准了T自身前后部分匹配的位置,那就可以改进算法。 如果不用回溯,那模式串下一个位置从哪里

8、开始呢? 还是上面那个例子,T(模式串)为ababc,如果c失配,那就可以往前移到aba最后一个a的位置,像这样:.ababd. ababc -ababc这样i不用回溯,j跳到前2个位置,继续匹配的过程,这就是KMP算法所在。这个当Tj失配后,j应该往前跳的值就是j的next值,它是由T串本身固有决定的,与S串(主串)无关。5、next数组的含义重点来了。下面解释一下next数组的含义,这个也是KMP算法中比较不好理解的一点。 令原始串为: Si,其中0=i=n;模式串为: Tj,其中0=j=m。 假设目前匹配到如下位置 S0,S1,S2,.,Si-j,Si-j+1.,Si-1,Si, Si+

9、1,.,SnT0,T1,.,Tj-1,Tj, . S和T的绿色部分匹配成功,恰好到Si和Tj的时候失配,如果要保持i不变,同时达到让模式串T相对于原始串S右移的话,可以更新j的值,让Si和新的Tj进行匹配,假设新的j用nextj表示,即让Si和nextj匹配,显然新的j值要小于之前的j值,模式串才会是右移的效果,也就是说应该有nextj = j -1。那新的j值也就是nextj应该是多少呢?我们观察如下的匹配: 1)如果模式串右移1位(从简单的思考起,移动一位会怎么样),即nextj = j - 1, 即让蓝色的Si和Tj-1匹配(注:省略号为未匹配部分) S0,S1,S2,.,Si-j,Si

10、-j+1.,Si-1,Si, Si+1,.,SnT0,T1,.,Tj-1,Tj, . (T的划线部分和S划线部分相等【1】)T0,T1,.Tj-2,Tj-1,. (移动后的T的划线部分和S的划线部分相等【2】) 根据【1】【2】可以知道当nextj =j -1,即模式串右移一位的时候,有T0 j-2 = T1 j-1,而这两部分恰好是字符串T0 j-1的前缀和后缀,也就是说nextj的值取决于模式串T中j前面部分的前缀和后缀相等部分的长度(好好揣摩这两个关键字概念:前缀、后缀,或者再想想,我的上一篇文章,从Trie树谈到后缀树中,后缀树的概念)。 2)如果模式串右移2位,即nextj = j

11、- 2, 即让蓝色的Si和Tj-2匹配 S0,S1,.,Si-j,Si-j+1,Si-j+2.,Si-1,Si, Si+1,.,SnT0,T1,T2,.,Tj-1,Tj, .(T的划线部分和S划线部分相等【3】)T0,T1,.,Tj-3,Tj-2,.(移动后的T的划线部分和S的划线部分相等【4】) 同样根据【3】【4】可以知道当nextj =j -2,即模式串右移两位的时候,有T0 j-3 = T2 j-1。而这两部分也恰好是字符串T0 j-1的前缀和后缀,也就是说nextj的值取决于模式串T中j前面部分的前缀和后缀相等部分的长度。 3)依次类推,可以得到如下结论:当发生失配的情况下,j的新值

12、nextj取决于模式串中T0 j-1中前缀和后缀相等部分的长度, 并且nextj恰好等于这个最大长度。 为此,请再允许我引用上文中的一段原文:“KMP算法中,如果当前字符匹配成功,即Si=Tj,令i+,j+,继续匹配下一个字符;如果匹配失败,即Si != Tj,需要保持i不变,并且让j = nextj,这里nextj =1), 同时移动之后,i之前的部分(即Si-j+1 i-1),和j=nextj之前的部分(即T0 j-2)仍然相等。显然,相对于BF算法来说,KMP移动更多的位数,起到了一个加速的作用!(失配的特殊情形,令j=nextj导致j=0的时候,需要将i +,否则此时没有移动模式串)。

13、” 于此,也就不难理解了我的关于KMP算法的第二篇文章之中:“当匹配到Si != Pj的时候有 Si-ji-1 = P0j-1. 如果下面用j_next去匹配,则有P0j_next-1 = Si-j_nexti-1 = Pj-j_nextj-1。此过程如下图3-1所示。 当匹配到Si != Pj时,Si-ji-1 = P0j-1:S: 0 i-j i-1i P:0 j-1j 如果下面用j_next去匹配,则有P0j_next-1 = Si-j_nexti-1 = Pj-j_nextj-1。所以在P中有如下匹配关系(获得这个匹配关系的意义是用来求next数组):P: 0 j-j_next .j-

14、1_ P:0 .j_next-1 所以,根据上面两个步骤,推出下一匹配位置j_next:S: 0 i-j i-j_next i-1i P:0 j_next-1j_next 图3-1 求j-next(最大的值)的三个步骤 下面,我们用变量k来代表求得的j_next的最大值,即k表示这Si、Pj不匹配时P中下一个用来匹配的位置,使得P0k-1 = Pj-kj-1,而我们要尽量找到这个k的最大值。”。 根据上文的【1】与【2】的匹配情况,可得第二篇文章之中所谓的k=1(如aaaa的形式),根据上文的【3】与【4】的匹配情况,k=2(如abab的形式)。 所以,归根究底,KMP算法的本质便是:针对待匹

15、配的模式串的特点,判断它是否有重复的字符,从而找到它的前缀与后缀,进而求出相应的Next数组,最终根据Next数组而进行KMP匹配。接下来,进入本文的第二部分。第二部分、next数组求法的来龙去脉与KMP算法的源码 本部分引自个人此前的关于KMP算法的第二篇文章:六之续、由KMP算法谈到BM算法。前面,我们已经知道即不能让Pj=Pnextj成立成立。不能再出现上面那样的情况啊!即不能有这种情况出现:P3=b,而竟也有Pnext3=P1=b。 正如在第二篇文章中,所提到的那样:“这里读者理解可能有困难的是因为文中,时而next,时而nextval,把他们的思维搞混乱了。其实next用于表达数组索

16、引,而nextval专用于表达next数组索引下的具体各值,区别细微。至于文中说不允许P=Pnextj 出现,是因为已经有P=b与S匹配败,而Pnext=P1=b,若再拿P1=b去与S匹配则必败。”-六之续、由KMP算法谈到BM算法。 又恰恰如上文中所述:“模式串T相对于原始串S向右移动了至少1位(移动的实际位数j - nextj =1)”。 ok,求next数组的get_nextval函数正确代码如下:view plain1./代码4-12./修正后的求next数组各值的函数代码3.voidget_nextval(charconst*ptrn,intplen,int*nextval)4.5.

17、inti=0;6.nextvali=-1;7.intj=-1;8.while(iplen-1)9.10.if(j=-1|ptrni=ptrnj)/循环的if部分11.12.+i;13.+j;14./修正的地方就发生下面这4行15.if(ptrni!=ptrnj)/+i,+j之后,再次判断ptrni与ptrnj的关系16.nextvali=j;/之前的错误解法就在于整个判断只有这一句。17.else18.nextvali=nextvalj;19.20.else/循环的else部分21.j=nextvalj;22.23. 举个例子,举例说明下上述求next数组的方法。S a b a b a b c

18、P a b a b cS4 != P4 那么下一个和S4匹配的位置是k=2(也即Pnext4)。此处的k=2也再次佐证了上文第3节开头处关于为了找到下一个匹配的位置时k的求法。上面的主串与模式串开头4个字符都是“abab”,所以,匹配失效后下一个匹配的位置直接跳两步继续进行匹配。S a b a b a b cP a b a b c匹配成功P的next数组值分别为-1 0 -1 0 2 next数组各值怎么求出来的呢?分以下五步:1.初始化:i=0,j=-1,nextval0 = -1。由于j = -1,进入上述循环的if部分,+i得i=1,+j得j=0,且ptrni != ptrnj(即a!=

19、b),所以得到第二个next值即nextval1 = 0;2.i=1,j=0,进入循环esle部分,j=nextvalj=nextval0=-1;3.进入循环的if部分,+i,+j,i=2,j=0,因为ptrni=ptrnj=a,所以nextval2=nextval0=-1;4.i=2, j=0, 由于ptrni=ptrnj,再次进入循环if部分,所以+i=3,+j=1,因为ptrni=ptrnj=b,所以nextval3=nextval1=0;5.i=3,j=1,由于ptrni=ptrnj=b,所以+i=4,+j=2,退出循环。 这样上例中模式串的next数组各值最终应该为: 图4-1 正确

20、的next数组各值next数组求解的具体过程如下: 初始化:nextval0 = -1,我们得到第一个next值即-1. 图4-2 初始化第一个next值即-1 i = 0,j = -1,由于j = -1,进入上述循环的if部分,+i得i=1,+j得j=0,且ptrni != ptrnj(即a!=b),所以得到第二个next值即nextval1 = 0;图4-3 第二个next值0 上面我们已经得到,i= 1,j = 0,由于不满足条件j = -1 | ptrni = ptrnj,所以进入循环的esle部分,得j = nextvalj = -1;此时,仍满足循环条件,由于i = 1,j = -

21、1,因为j = -1,再次进入循环的if部分,+i得i=2,+j得j=0,由于ptrni = ptrnj(即ptrn2=ptrn0,也就是说第1个元素和第三个元素都是a),所以进入循环if部分内嵌的else部分,得到nextval2 = nextval0 = -1; 图4-4 第三个next数组元素值-1 i = 2,j = 0,由于ptrni = ptrnj,进入if部分,+i得i=3,+j得j=1,所以ptrni = ptrnj(ptrn3=ptrn1,也就是说第2个元素和第4个元素都是b),所以进入循环if部分内嵌的else部分,得到nextval3 = nextval1 = 0;图4-

22、5 第四个数组元素值0 如果你还是没有弄懂上述过程是怎么一回事,请现在拿出一张纸和一支笔出来,一步一步的画下上述过程。相信我,把图画出来了之后,你一定能明白它的。 然后,我留一个问题给读者,为什么上述的next数组要那么求?有什么原理么?提示:我们从上述字符串abab 各字符的next值-1 0 -1 0,可以看出来,根据求得的next数组值,偷用前缀、后缀的概念,一定可以判断出在abab之中,前缀和后缀相同,即都是ab,反过来,如果一个字符串的前缀和后缀相同,那么根据前缀和后缀依次求得的next各值也是相同的。5、利用求得的next数组各值运用Kmp算法 Ok,next数组各值已经求得,万事

23、俱备,东风也不欠了。接下来,咱们就要应用求得的next值,应用KMP算法来匹配字符串了。还记得KMP算法是怎么一回事吗?容我再次引用下之前的KMP算法的代码,如下:view plain1./代码5-12./intkmp_seach(charconst*,int,charconst*,int,intconst*,intpos)KMP模式匹配函数3./输入:src,slen主串4./输入:patn,plen模式串5./输入:nextvalKMP算法中的next函数值数组6.intkmp_search(charconst*src,intslen,charconst*patn,intplen,intc

24、onst*nextval,intpos)7.8.inti=pos;9.intj=0;10.while(islen&j=plen)25.returni-plen;26.else27.return-1;28.我们上面已经求得的next值,如下:图5-1 求得的正确的next数组元素各值 以下是匹配过程,分三步: 第一步:主串和模式串如下,S3与P3匹配失败。图5-2 第一步,S3与P3匹配失败 第二步:S3保持不变,P的下一个匹配位置是Pnext3,而next3=0,所以Pnext3=P0,即P0与S3匹配。在P0与S3处匹配失败。图5-3 第二步,在P0与S3处匹配失败 第三步:与上文中第3小节末的情况

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1