字符串匹配问题。假设文本是一个长度为$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算法入门讲解的更多相关文章

  1. 【面向打野编程】——KMP算法入门

    一.问题 咱们先不管什么KMP,来看看怎么匹配两个字符串. 问题:给定两个字符串,求第二个字符串是否包含于第一个字符串中. 为了具体化,我们以 ABCAXABCABCABX 与 ABCABCABX为例 ...

  2. 【初识】KMP算法入门(转)

    感觉写的很好,尤其是底下的公式,易懂,链接:http://www.cnblogs.com/mypride/p/4950245.html 举个例子 模式串S:a s d a s d a s d f a  ...

  3. 字符串匹配KMP算法的讲解C++

    转自http://blog.csdn.net/starstar1992/article/details/54913261 也可以参考http://blog.csdn.net/liu940204/art ...

  4. 【初识】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 ...

  5. 一篇别人写的Kmp算法的讲解,多看多得

    kmp算法的理解与实现 博客分类: algorithms 算法      KMP算法曾被我戏称为看毛片算法,当时笑喷......大三那个时候硬着头皮把算法导论的kmp算法啃完,弄懂了kmp算法 的原理 ...

  6. KMP算法入门

    学一把看毛片算法我觉得自己才能变得更加出色 明明昨天的题我都知道怎么模拟了,但是还是不会改KMP,是我学丑了 KMP是Knuth-Morris-Pratt三人设计的线性时间字符串匹配算法 nxt数组的 ...

  7. KMP算法总结

    kmp算法的T子字符串的下标的变化规律 大话数据结构这边书中的KMP算法的讲解跟最终的算法代码还是有很大的差别 java语言只会if判断语句,循环语句,但是这些语句以及可以包罗万象了,可以适用很多情况 ...

  8. 关于KMP算法理解(快速字符串匹配)

    参考:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 2016-08- ...

  9. KMP算法的优化与详解

    文章开头,我首先抄录一些阮一峰先生关于KMP算法的一些讲解. 下面,我用自己的语言,试图写一篇比较好懂的 KMP 算法解释. 1. 首先,字符串"BBC ABCDAB ABCDABCDABD ...

随机推荐

  1. 2017年第八届蓝桥杯国赛试题(JavaA组)

    1.结果填空 (满分19分)2.结果填空 (满分47分)3.代码填空 (满分21分)4.程序设计(满分35分)5.程序设计(满分79分)6.程序设计(满分99分) 1.标题:图书排列 将编号为1~10 ...

  2. 将Gridview导出到Excel

    GridViewToExcel(EdceExcelGV, "application/ms-exce","xxxxxx表"); protected void Gr ...

  3. PHP之递归函数

    https://www.cnsecer.com/4146.html http://www.jb51.net/article/71424.htm //一列数字的规则如下:1,1,2,3,5,8,13,2 ...

  4. codeforces757F Team Rocket Rises Again【支配树+倍增+拓扑+spfa】

    先跑spfa求出最短路构成的DAG,然后在DAG上跑出支配树dfs出size取max即可 关于支配树,因为是DAG,支配点就是入点在支配树上的lca,所以一边拓扑一边预处理倍增,然后用倍增求lca # ...

  5. [Xcode 实际操作]六、媒体与动画-(15)使用AudioPlayer播放音乐

    目录:[Swift]Xcode实际操作 本文将演示如何播放音频素材. 在项目名称上点击鼠标右键,弹出右键菜单, 选择[Add Files to "DemoApp"],往项目中导入文 ...

  6. IOS 版本控制判断

    // 版本判断#define SYSTEM_VERSION(ver) [[[UIDevice currentDevice] systemVersion] compare:ver] != NSOrder ...

  7. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree 倍增 最小生成树

    好吧我太菜了又调了一晚上...QAQ 先跑出最小生成树,标记树边,再用树上倍增的思路,预处理出: f[u][i] :距离u为2^i的祖先 h[u][i][0/1] :距u点在2^i范围内的最长边和次长 ...

  8. CoreRT

    使用CoreRT将.NET Core发布为Native应用程序 在上一篇文章<使用.NET Core快速开发一个较正规的命令行应用程序>中我们看到了使用自包含方式发布的.NET Core应 ...

  9. mysql 启动停止脚本 and mysql 迁移 导入和导出

    ####监控脚本 [root@pdb~]# more /opt/VRTS/scripts/mysql_monitor.sh#!/bin/shn=`ps -ef |grep mysql|grep &qu ...

  10. es6新语法系列,查找字符串,模板字符串

    一.模板字符串: ES6引入了一种新型的字符串字面量语法,我们称之为模板字符串(template strings).除了使用反撇号字符 ` 代替普通字符串的引号 ' 或 " 外,它们看起来与 ...