KMP算法入门讲解
字符串匹配问题。假设文本是一个长度为$n$的字符串$T$,模板是一个长度为$m$的字符串$P$,且$m\leq n$。需要求出模板在文本中的所有匹配点$i$,即满足$T[i]=P[0],T[I+1]=P[1],...,T[m-1]=P[m-1]$的非负整数$i$(注意字符串下标从0开始)。如图所示,$P$在$T$中有且只有一个匹配点,即位置3。

最朴素的方法是依次判断每个位置$s$是不是一个匹配点。检查匹配点需要$O(m)$时间,而可能的匹配点有$O(n-m)$个,所以最坏情况时间复杂度为$O(nm)$。有一个简单的优化:在检查匹配点的合法性是只要有一个字符不同,立刻停止比较,换下一个匹配点。但最坏情况下时间复杂度没变。
和朴素算法相比,KMP算法的时间效率就强多了。它首先用$O(m)$的时间对模板进行预处理,然后用$O(n)$的时间完成匹配。从渐进意义上来说,这样的时间复杂度已经是最好的了(至少需要$O(m+n)$时间,因为至少需要检查文本串和模板的每个字符)。
虽然代码很短,但KMP的细节并不容易理解。考虑到网上已经有很多介绍KMP的资料,这里只对它进行简单介绍,作为学习Aho-Corasick自动机的铺垫。
KMP算法的精髓蕴含在下图中。

假设在匹配过程中正在比较文本字符串*位置的字符和模板字符串abbaaba的最后一个字符,发现两者不同(称为失配),这时,朴素算法只会把模式串右移一位,重新比较abbaaba的第一个字符和文本串!!位置的字符。
KMP算法认为,既然!!位置已经比较过一次了,就不应该再比一次。事实上,我们已经知道灰色部分就是abbaab,应该可以直接利用模板串本身的特性判断出右移一位一定不是匹配的。同理,右移两位或者三位也不行,但是右移四位是有可能的。这个时候,需要比较*处的字符和abbaaba的第三个字符。
上图那条链是一个状态自动机,其中编号为$i$的结点表示已经匹配了$i$个字符。匹配开始时当前状态是0,成功匹配时状态加1(表示多匹配了一个字符),而失配时沿着“失配边”走。比如在这个例子中,如果在状态6时失配,应该转移到状态2.为了方便起见,这里用失配函数(failure function)$F[i]$表示状态$i$失配时应转移到的新状态,要特别注意的是$f[0]=0$。
有了失配函数后,KMP算法不难写出,代码如下:
void find(char* T, char* P, int * f)
{
int n = strlen(T), m = strlen(P);
getFail(P, f);
int j = ; //当前结点编号
for (int i = ; i < n; i++)
{
while (j && P[j] != T[i]) j = f[j]; //顺着失配边走,知道可以匹配
if (P[j] == T[i]) j++;
if (j == m) printf("%d\n", i - m + ); //找到了一个
}
}
这个代码的时间复杂度如何?答案可能并不明显。失配的时候也许会反复向左走很多次,会不会太慢?不会。可以这样计算时间复杂度。每次$j{++}$的时候伴随一个$i{++}$,而每次$j=f[j]$的时候$j$至少会减1。最坏情况下$j$增加了$n$次,因此$j=f[j]$的次数不会超过$n$,因此总时间复杂度为$O(n)$。
状态转移图的构造是KMP算法的关键,也是它最巧妙的地方。算法的思想是“用自己匹配自己”,根据$f[0],f[1],...,f[i-1]$递推$f[i]$,代码和匹配部分非常相似,如下所示。
void getFail(char* P, int* f)
{
int m = strlen(P);
f[] = ; f[] = ; //递推边界的初值
for (int i = ; i < m; i++)
{
int j = f[i];
while (j && P[i] != P[j]) j = f[j]; //往回走
f[i + ] = (P[i] == P[j] ? j + : );
}
}
KMP算法入门讲解的更多相关文章
- 【面向打野编程】——KMP算法入门
一.问题 咱们先不管什么KMP,来看看怎么匹配两个字符串. 问题:给定两个字符串,求第二个字符串是否包含于第一个字符串中. 为了具体化,我们以 ABCAXABCABCABX 与 ABCABCABX为例 ...
- 【初识】KMP算法入门(转)
感觉写的很好,尤其是底下的公式,易懂,链接:http://www.cnblogs.com/mypride/p/4950245.html 举个例子 模式串S:a s d a s d a s d f a ...
- 字符串匹配KMP算法的讲解C++
转自http://blog.csdn.net/starstar1992/article/details/54913261 也可以参考http://blog.csdn.net/liu940204/art ...
- 【初识】KMP算法入门
举个例子 模式串S:a s d a s d a s d f a s d 匹配串T:a s d a s d f 如果使用朴素匹配算法—— 1 2 3 4 5 6 8 9 a s d a s d a s ...
- 一篇别人写的Kmp算法的讲解,多看多得
kmp算法的理解与实现 博客分类: algorithms 算法 KMP算法曾被我戏称为看毛片算法,当时笑喷......大三那个时候硬着头皮把算法导论的kmp算法啃完,弄懂了kmp算法 的原理 ...
- KMP算法入门
学一把看毛片算法我觉得自己才能变得更加出色 明明昨天的题我都知道怎么模拟了,但是还是不会改KMP,是我学丑了 KMP是Knuth-Morris-Pratt三人设计的线性时间字符串匹配算法 nxt数组的 ...
- KMP算法总结
kmp算法的T子字符串的下标的变化规律 大话数据结构这边书中的KMP算法的讲解跟最终的算法代码还是有很大的差别 java语言只会if判断语句,循环语句,但是这些语句以及可以包罗万象了,可以适用很多情况 ...
- 关于KMP算法理解(快速字符串匹配)
参考:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 2016-08- ...
- KMP算法的优化与详解
文章开头,我首先抄录一些阮一峰先生关于KMP算法的一些讲解. 下面,我用自己的语言,试图写一篇比较好懂的 KMP 算法解释. 1. 首先,字符串"BBC ABCDAB ABCDABCDABD ...
随机推荐
- SQL Server(五)——常用函数 转
1.数学函数:操作一个数据,返回一个结果 --取上限ceiling select code,name,ceiling(price) from car ; --取下限 floor select floo ...
- EF中外键重命名,打破原先的约束规则
本人建议玩code frist的人多用用System.ComponentModel.DataAnnotations和System.ComponentModel.DataAnnotations.Sche ...
- .Net HttpWebRequest 爬虫核心爬取
1 爬虫,爬虫攻防 2 下载html 3 xpath解析html,获取数据和深度抓取(和正则匹配) 4 多线程抓取 熟悉http协议 提供两个方法Post和Get public static stri ...
- codeforces704D Captain America【上下界最大流】
分别给行和列hash建两排点,对(x,y)坐标连x行y列的点 设红色价格低,那么就要尽量多选红色 设一个点出度为s,要求最小的最大差值为d,又,假设有流量表示选红没流量表示选蓝,那么要求就变成了这个点 ...
- InnoDB Monitors | SHOW ENGINE INNODB STATUS
参考 <mysql 5.7手册>,15.17章节 InnoDB监视器提供有关InnoDB内部状态的信息. 这些信息对于性能调优非常有用.其实所谓的监视器,就是 show engine in ...
- AcDbSymbolTable of AcDbDatabase
AcDbBlockTable AcDbLayerTable AcDbTextStyleTable AcDbLinetypeTable AcDbViewTable AcDbUCSTable AcDbVi ...
- 阿里云服务器新手安装nginx
1.域名购买之后 备案. 2.购买服务器. 3.这两个条件具备之后进行下一步. 本人使用Mac ,实践流程按照Mac本的流程操作. 作为初学者,简配的服务器, 实例类型: I/O优化,操作系统: Ce ...
- 手工sql注入判断是否存在注入点
1.加入单引号 ’提交,结果:如果出现错误提示,则该网站可能就存在注入漏洞.2.数字型判断是否有注入;语句:and 1=1 ;and 1=2 (经典).' and '1'=1(字符型)结果:分别返回不 ...
- react-native-syan-image-picker的使用
传送门 第一种方式:link 第一步:安装 1. npm install react-native-syan-image-picker --save 2. react-native link re ...
- Python-12-简单推导
列表推导:从其他列表创建列表 >>> [x * x for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 下面实现只打印能 ...