KMP算法
2020-12-24 00:29
标签:连续 转移 思想 基本 kmp 空间 后缀 假设 区间 KMP,作为字符串的入门算法,还是比较有难度的。起码当初我尝试理解KMP的时候,就花了整整一个上午去翻阅各种博客。虽然每一篇博客在理解之后再去看会发现说得都挺有道理,但是在云里雾里的时候,并不是所有的博客都能一语点破雾水。 特将学习体会记录下来(>人<;) KMP算法,是一种字符串的单模匹配算法。说白了,就是在s1串中去找s2串。 看吧,其实名字高大上,本质上就是判重.. 假如没有KMP,我们要怎么处理这种问题? 枚举每一个开头,然后从每一个开头来尝试是否是字串? 参照下面这个例子。 这个例子比较极端,但是能比较好的阐述KMP的优越性 好了,我们尝试用朴素算法处理单模匹配。 可以发现,假如数据更加极端,朴素算法的效率也会成倍的放大。在最劣情况下,复杂度可以达到O(s1长度\(\times\)s2长度),并且这种数据也是相当好造(上述就是一个例子),所以说朴素算法基本上可以被废弃了 回顾刚刚的匹配过程,哪里可以优化呢? 可以发现,辛辛苦苦匹配了很长的“已匹配区间”,仅仅因为一个“失配因子”,就要全部推翻重来。并且在下一次的匹配中,上一次的“已匹配区间”完全没能利用上。 这部分“已匹配空间”虽然占用了匹配时间,但是仅仅发挥了一次作用。很明显,这种匹配思路不尽合理。我们应当想法设法,把所以已匹配上的信息利用好。 对于例子中给出的“已匹配区间”,我们发现左面有连续的4个abab,右面也有连续的4个abab。假如我们下一次直接从右面的abab开始接着匹配,那么就省下了重复匹配这个abab的过程。 在此,引入一个概念 “最长公共前后缀” 前缀是什么? 前缀就是指对于长度为n的字符串,从前面开始长度为\(1 \rightarrow (n-1))\)的所有字串 "tesn"的所有前缀依次为:t,te,tes 后缀是什么? 后缀就是指对于长度为n的字符串,从后面开始长度为\(1 \rightarrow (n-1))\)的所有字串 "test"的所有后缀依次为:esn,sn,n 那么,什么是公共前后缀? 顾名思义,就是在全部前后缀中,相同的一对 ababab的前缀依次为:a,ab,aba,abab,ababa 后缀依次为:babab,abab,bab,ab,b 那么它的公共前后缀就有:ab,abab 最长公共前后缀呢?就是“最长”的公共前后缀 例如,ababab的最长公共前后缀就是abab 了解最长公共前后缀之后,再回到匹配区间上。 在一段匹配区间中,由于前缀与后缀有相同部分,于是后缀便可以直接用作下一个区间的前缀。类似于整体平移 为了最大化利用匹配区间上已经匹配过的信息,我们就找到当前区间的最长公共前后缀。这样一来,下一次匹配就有了经可能大的“基础”,匹配起来就会快一些 (到这一步,初学者可能会有些困惑,甚至举出了挑战正确性的“反例”。坚持看下去!( o=^?ェ?)o ┏━┓) 我们把在每一个节点失配时,应当跳向的下一个节点取名为当前节点的失配指针。失配指针指向的便是字串的最长公共前后缀的末坐标。 注意!失配指针指的不是当前节点的失配,而是当前节点的下一个节点失配时的处理情况。 怎么批处理出失配指针?分类讨论 假设当前匹配到了u节点 · 如果u+1节点不等于fail[u]+1节点! 怎么办?不慌!由于公共前后缀的公共前后缀同样满足公共的性质,所以我们递归即可。 · 那假如到头也没能找到公共前后缀?那就将它的失配指针指向root=0即可 嗯,思路就是这样,代码如下: 有了,失配指针,怎么快速匹配? KMP的另一个精髓就是:只有s2指针可能回档,s1指针只会前进 如果匹配上了: 反之如果失配了,就要跳失配指针: 酱紫匹配下去,当s2指针==s2长度时,就说明成功匹配了一个字串!( ̄y▽, ̄)╭ strlen的效率并不是非常高,请无比多声明一个len变量,而不要每一次都调用strlen()函数。否则一个正确的算法可能会因为这一个小点而tle6个点 当成功在s1串中匹配到一个s2串之后,如果还需要继续匹配,可以为s2指针来一个失配转移,而不是直接归为0。算是小优化技巧 字符串的最小循环节嘛,定义没什么好说的 一个重要的性质: \(字符串存在长度为s的循环节 \iff s(l,r-s) = s(l+s,r)\) 应该很好理解吧( ̄︶ ̄*)) 那么假如字符串存在最小的循环节,长度为\(s‘\),那么就存在"最长的公共前后缀"\(s(l,r-s‘) = s(l+s‘,r)\) 而失配指针求的正是这个“最长公共前后缀”。如此一来不就一石二鸟了嘛 反之,如果字符串不存在循环节,那么整个字符串长度一定不能被公共前后缀余下的区间长度整除 KMP第一次学有些难以理解,但是好好理解理解,还是一个好写好调的模板 至此,KMP总结完毕!(〃` 3′〃) KMP算法 标签:连续 转移 思想 基本 kmp 空间 后缀 假设 区间 原文地址:https://www.cnblogs.com/ticmis/p/13211109.html字符串 KMP算法
前言
KMP用来干什么?
朴素算法的低效性
KMP算法的优越性
最长公共前后缀思想
失配指针
· 如果u+1节点与fail[u]+1节点相同,那么fail[u+1]=fail[u]+1// 第一个不用建,因为fail肯定指向root=0
for(int i=2;i
匹配
技巧或忠告
完整代码
#include
应用:求字符串的最小循环节
后记