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 ...
随机推荐
- ABP 软删除ISoftDelete
一.简介 ABP 的软删除是为了,在删除的时候,不是真正的删除数据,是为了保护数据. 二.具体实现 在 Core 层,我们需要这个实体去实现这个 ISoftDelete 接口.实现它的 public ...
- SCUT - 278 - 谜题#020 - 左偏树
https://scut.online/p/278 第一次遇到不需要并查集的左偏树. #include<bits/stdc++.h> using namespace std; typede ...
- 《深度学习-改善深层神经网络》-第二周-优化算法-Andrew Ng
目录 1. Mini-batch gradient descent 1.1 算法原理 1.2 进一步理解Mini-batch gradient descent 1.3 TensorFlow中的梯度下降 ...
- ue4 官网IK流程记录
基本流程 角色蓝图构造 角色蓝图 角色蓝图中新建的函数IK Foot Trace AnimGraph事件 这里注意下Make Vector时把z方向的偏移量设置到了X上 猜测原因是效应器的x方向跟世界 ...
- ue4 杂记
c++获取GameMode if(GetWorld()) { auto gamemode = (ASomeGameMode*)GetWorld()->GetAuthGameMode(); } 或 ...
- 解决resignFirstResponder或者endEditing无效的办法
当你想要收回弹出的键盘时却发现平时用的resignFirstResponder和endEditing都失去作用时,应该考虑一下当前的TextField是否为第一响应者,如果不是第一响应者的话,自然下面 ...
- PJzhang:子域名发掘工具Sublist3r
猫宁!!! 参考链接:https://www.freebuf.com/sectool/90584.html 作者上一次更新是2018年10月16日了,sublist3r中融合有另外一个子域名爆破工具S ...
- 微信小程序之登录
微信小程序登录基本流程就是 1. wx.login获取js_code 根据app_id, secret, js_code 数据 wx.request 获取用户的openid和session_key ...
- 条件分页 分页条件和页参数传递方式一 超链接拼串 方式二 使用查询表单searchForm
<%-- Created by IntelliJ IDEA. User: jie Date: 2019/5/10 Time: 20:00 To change this template use ...
- Luogu P2341 [HAOI2006]受欢迎的牛 SCC缩点
把强连通分量缩点,如果有且仅有一个出度为0的强连通分量,那么答案就是他的size:如果有多个入度为0的,那么没有明星牛. #include<cstdio> #include<iost ...