字符串匹配问题。假设文本是一个长度为$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. .Net HttpWebRequest 爬虫核心爬取

    1 爬虫,爬虫攻防 2 下载html 3 xpath解析html,获取数据和深度抓取(和正则匹配) 4 多线程抓取 熟悉http协议 提供两个方法Post和Get public static stri ...

  2. view如何从action中取得数据和 Html辅助方法

    方式:1使用弱类型取,2,使用强类型,两者的差别在于view页面最上方声明的方式   如果使用弱类型接受来自控制器的数据,在view页面里完全不需要有任何的生命,数据可以从ViewData,ViewB ...

  3. nginx是如何处理一个请求的(包含https配置)

    配置https首先要有ssl证书,这个证书目前阿里有免费的,但如果自己做实验,也是可以自签证书,只不过不受信 openssl genrsa -des3 -out server.key 1024     ...

  4. ue4 svn备份目录

    http://blog.csdn.net/sh15285118586/article/details/55737480 UE4工程文件备份目录有:Config.Content.Plugins.Sour ...

  5. Hadoop 2.7.3 HA 搭建及遇到的一些问题

    看了Hadoop的一个7天视频教程,里面给出了搭建的详细步骤,教程中是按2.4.1版本搭建的,我用的是2.7.3版本,好像没什么差别.下面是抄过来的,加了一点注释. hadoop2.0已经发布了稳定版 ...

  6. 从图(Graph)到图卷积(Graph Convolution):漫谈图神经网络模型 (二)

    本文属于图神经网络的系列文章,文章目录如下: 从图(Graph)到图卷积(Graph Convolution):漫谈图神经网络模型 (一) 从图(Graph)到图卷积(Graph Convolutio ...

  7. webpack 打包和手动创建一个vue的项目

    首先我们为啥要用webpack,为啥不用其他的打包的工具. 先听我捋捋, Webpack有人也称之为 模块打包机 ,由此也可以看出Webpack更侧重于模块打包,当然我们可以把开发中的所有资源(图片. ...

  8. ios 适配问题

    两张图解决

  9. according to tld or attribute directive in tag file attribute *** does not accept any expressions

    http://stackoverflow.com/questions/13428788/according-to-tld-or-attribute-directive-in-tag-file-attr ...

  10. [题解](同余)POJ_3696_The Luckiest Number

    还是挺难的吧......勉强看懂调了半天 首先表达式可以写成 8(10^x -1)/9,题意为求一个最小的x使L | 8(10^x -1)/9 设d=gcd(L,8) L | 8(10^x -1)/9 ...