KMP

来源:互联网 发布:天刀慕情捏脸数据 编辑:程序博客网 时间:2024/06/15 03:02

算法:kmp

编号:0001

时间:171009

 

<<<引入>>>

首先,看问题:

    S是一个很长的字符串,t是个短短的字符串,现在要从S中找到t,如何找?

 

一个一个比那是傻子,一般的做法是从第一个开始先匹配第一个字符,然后第二个.....依次匹配,其做法就类似于将S每个字符写在一张卡片上,然后从第一个字符开始依次一个一个展示给你看,让你找到t 。

 

而kmp算法则是一种不回溯的算法,也就是说,不需要回头

 

你看卡片的时候,假设遇到下面这样的情况:

  S : abcabcabe

  t :   abcabe

显然,abcabc不能和t 匹配,因为当你满心欢喜地匹配上了abcab的时候,下一张卡片却是c而不是e,接下来,怎么办?

这时,你必须要求展示卡片的人:“诶,不好意思,再从刚才的a(第四张卡片)开始展示”,不然的话,你的答案是错的。

这就是回头,即回溯。

 

<<<算法的详细叙述>>>

kmp算法就提供了从头展示到尾即可的一种寻找法,下面我不厌其烦地介绍以下整个流程,稍后我会再提出一个浓缩版:

【一】首先,我们需要个预处理

1.我们必须弄明白,看到什么情况我们会认定:“卧槽,匹配失败了”?当然,是看到两个不一样的字符,所以,不一样的字符是我们的核心,设S[ i ] != t [ m ]

2.接下来,还需要明白一个观点,那就是“跳”的意思,如上面的例子,假设我们在匹配失败后,跳到第四位,则匹配成功,跳就是移动关注S位置的过程

3.在正式开始之前,我们定义一个next数组,这个数组将会告诉我们,跳的位置,不过我们定义跳的距离为 m - next [ m ]

>>>下面开始介绍算法:

【二】匹配的所有可能情况:(其中m是被发现不同的地方)

1.如果遇到以下情况,next [ m ]  规定为-1:

     ①.t[m]==t[0]并且 m前的 前缀和后缀完全不同(比如abcdef的前缀{a,ab,abc,abcd,abcde}和后缀{f,ef,def,cdef,bcdef}完全没有交集)

     ②.t[m]==t[0],有长度为k的前缀和后缀相同,但t[m]==t[k] (比如abcdabcd中,前缀abcd与后缀相同但t[k]==t[m]=='d')

     ③.m==0 第一个没有前缀后缀,必须规定为-1,是初始化的操作

2.如果遇到以下情况,next [ m ] 规定为k:(k就是情况1中的k)

     ①.有长度为k的前缀和后缀相同,t[m] != t[k] ,也就是1.②中的一个对立,不过要注意,没有说t[m]==t[0]这个条件!

3.其他情况,一律规定为0

【三】遍历S

1.一旦找到一个S[ i ] != t [ m ],记录下这个m,当对应的next[m]为-1时,直接跳到S的下一个字符开始重新与t的第一个字符比较,不是-1时,用m处的字符开始和t [ next [ j ] ] 开始比较

2.比较过程中,又找到了新的不同,重新进行第一步

3.遍历到S的末端或匹配成功,结束

 

以上就是该算法的全部内容

 

<<<原理理解>>>

如果想要知道原理,以下是我的一点点通俗见解,嫌麻烦就别看了

1.怎么就更快了?

实际上,该算法快的原因是,在比较的过程中,已经比过的就记下来了,不用再比了。比如t的第一位和第四位都是4,第四位匹配成功了,那么第一位被移到那里时,便不需要再匹配了

2.k是什么东西?

k的实际意义我认为是比较和移动数的差值,通俗说,就是虽然我移动到这里,但并不是从这里开始比较,比如abcabeabe和abeabe的比较中,我已经知道t的1、2、3位和4、5、6位相同,而4、5、6位和S的4、5、6位相同,虽然我移动到4、5、6位,但是比较从7、8、9位开始即可,这中间的差值就是k(k=3),它产生于“已经知道比较结果就不用再比较了”

3.为什么要移动?移动有什么意义?

移动没什么实际意义,只是一种类似于指针的作用,方便表示当前需要比较的S位置在哪里,而算法的核心在于k。通俗地说,移动只是调整一张纸带的位置,方便叙述我们现在比到哪儿了,而k才是指导性的指令,指引我们接下来要从哪里开始比。

4.next数组和什么有关系?

next数组和要找的t有关系,由于t的特殊性,我们可以少比较很多,它和S没有关系,因为S本身是机械的

5.比较的时候有什么玄机?为什么一会儿要移动一位,一会儿又不移动?一会儿是0一会儿是next[m]?

比较的时候,S[i+1]是说S[i]已经没有必要比较了,从下一位开始新的比较,是一次完全地“重来”  比如abcgabcd和abcd比较,g的出现使得abcg的比较失去意义

而S[i]说明此处依然有必要进行比较,可能前面几个匹配上了,是一次部分地“重来”  比如abcabcabe和abcabe的比较,虽然abcabc不匹配,但后三个abc是有意义的,事实上正是后三个abc和再后面的abe组成成功的匹配

这就是两种移动的区别。

 

<<<编程套路>>>

以下为算法的浓缩版,也是编程时的依据:

 

 1.遍历t,按照下面的规律给每一个next [ m ] 赋值

next[m]当:

①.t[m]==t[0]并且 t[m]前的那部分 前缀和后缀完全不同

②.t[m]==t[0],有长度为k的前缀和后缀相同,但t[m]==t[k]

③.m==0

赋值为-1

 

next[j]当:

①.有长度为k的前缀和后缀相同,t[m] != t[k]

赋值为k

 

都不是:(一般是没有上面这么特殊的,这才是最多数的情况)

赋值为0

 

2.从S第一位开始逐位比较,直到S[i]!=t[m]出现,执行以下指令:

①.当前位置向右移动m-next[m]位

②若next[m]==-1,从S[i+1]开始与t[0]执行下一次逐位比较

   若next[m]=p>=0,从S[i]开始与t[p]执行下一次逐位比较

 

3.S比较至尽头或匹配成功,搜索结束

 

 

若有错误或还有什么疑问,欢迎提出~

原创粉丝点击