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比较至尽头或匹配成功,搜索结束
若有错误或还有什么疑问,欢迎提出~
- KMP
- KMP
- KMP
- KMP
- KMP
- KMP
- kmp
- kmp
- KMP
- kmp
- KMP
- KMP
- KMP
- kmp
- KMP
- KMP
- kmp
- KMP
- MySql的字符串函数
- Mac下更新node版本
- [python]谈谈 if __name__ == '__main__'
- Android中View的事件分发和拦截机制
- MySQL 5.0.96 win32 绿色精简版
- KMP
- sort简单题
- 火狐浏览器好用的插件
- Git入门(二)
- Linux的一些基础操作(不定时补充)
- java的导出Excel表格
- 博饼开奖
- 回文--栈-《数据结构与算法》
- bzoj1334[Baltic2008]Elect 背包dp