`
kenby
  • 浏览: 716409 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

kmp算法的理解与实现

阅读更多

    KMP算法曾被我戏称为看毛片算法,当时笑喷......大三那个时候硬着头皮把算法导论的kmp算法啃完,弄懂了kmp算法

的原理,甚至还写出了代码,这几天再次温习的时候,发现忘得比较彻底。我总结,学算法不能只对着书本学理论,而应该

用自己的理解去看清算法的本质,最好用文字把你的理解记录下来,这样才能做到活学活用,而且不容易忘。写这篇博客就是想把自己这几天的思路记下来。

 

一 kmp算法为什么比传统的字符串匹配算法快

假设文本T = y1y2y3....yn, 模式 P = p1p2p3...pm, 传统的匹配算法把位移为0,1,...n-m时的文本依次跟P比较,每次比较最多花费O(m)的时间,算法的复杂度为O((n-m+1)*m)。这种算法没有利用匹配过的信息,每次都从头开始比较,速度很慢。而kmp算法充分利用了之前的匹配信息,从而避免一些明显不合法的位移。加快匹配过程。来看一个例子:

 

#########000xxxx000######                       文本T

|<---- s ---->|000xxxx000~~~                              模式P

 

假设位移为s时,T和P匹配了红色部分的字符,即匹配到了模式P的前10个字符,如果按照传统的匹配方法,下一步就是从位移s+1开始比较,而kmp算法则直接从位移s+7开始比较,而且断定:位移s+7对应的串和模式P的前3个字符是相同的,可

以不用比较,直接从第4个字符开始比较,这种跳跃式的匹配是不是比传统匹配方法快很多,如下图所示:

 

#########000xxxx000######                       文本T

|<-------- s+7-------->| 000xxxx000~~~               模式P

 

 

 

那么kmp是如何实现这种跳跃的呢?注意到红色部分的字符,即模式P的前10个字符,有一个特点:它的开始3个字符和末尾

3个字符是一样的,又已知文本T也存在红色部分的字符,我们把位移移动 10-3 = 7个位置,让模式P的开始3个字符对准文本

T红色部分的末尾3个字符,那么它们的前3个字符必然可以匹配。

 

二 构造前缀数组

 

上面的例子是文本T和模式P匹配了前面10个字符的情况下发生的,而且我们观察到模式P的前缀P10中,它的开始3个字符和末尾3个字符是一样的。如果对于模式P的所有前缀P1,P2...Pm,都能求出它们首尾有多少个字符是一样的,当然相同的字

符数越多越好,那么就可以按照上面的方法,进行跳跃式的匹配。

 

 

定义:

Pi表示模式P的前i个字符组成的前缀, next[i] = j表示Pi中的开始j个字符和末尾j个字符是一样的,而且对于前缀Pi来说,这样

的j是最大值。next[i] = j的另外一个定义是:有一个含有j个字符的串,它既是Pi的真前缀,又是Pi的真后缀

 

规定:

next[1] = next[0] = 0

 

 

next[i]就是前缀数组,下面通过1个例子来看如何构造前缀数组。

例子1:cacca有5个前缀,求出其对应的next数组。

前缀2为ca,显然首尾没有相同的字符,next[2] = 0

前缀3为cac,显然首尾有共同的字符c,故next[3] = 1

前缀4为cacc,首尾有共同的字符c,故next[4] = 1

前缀5为cacca,首尾有共同的字符ca,故next[5] = 2

 

 

 

如果仔细观察,可以发现构造next[i]的时候,可以利用next[i-1]的结果。假设模式已求得next[10] = 3,如下图所示:

 

 

000#xxx000         前缀P10

000                        末尾3个字符

 

根据前缀函数的定义:next[10] = 3意味着末尾3个字符和P10的前3个字符是一样的

为求next[11],可以直接比较第4个字符和第11个字符,如下图所示:蓝色和绿色的#号所示,如果它们相等,则

next[11] = next[10]+1 = 4,这是因为next[10] = 3保证了前缀P11和末尾4个字符的前3个字符是一样的.

 

000#xxx000#       前缀P11

000#                      末尾4个字符

 

所以只需验证第4个字符和第11个字符。但如果这两个字符不想等呢?那就继续迭代,利用next[next[10] = next[3]的值来求

next[11]。代码如下:

 

void compute_prefix(int *next, char *p)
{
	int		i, n, k;

	n = strlen(p);
	next[1] = next[0] = 0;
	k = 0;		/* 第i次迭代开始之前,k表示next[i-1]的值 */	
	for (i = 2; i <= n; i++) {
		for (; k != 0 && p[k] != p[i-1]; k = next[k]);
		if (p[k] == p[i-1]) k++;
		next[i] = k;
	}
}
 

 

三 模拟KMP的查找过程

这里实现的算法与算法导论中的不一样,我觉得这种方法更加直观,思路就是模拟第1部分介绍的方法,每次匹配的时候

都利用上一次匹配信息,对于模式P,从第next[q]个字符开始比较,对于文本T,用一个变量s指示将要从哪个位置开始比较

,迭代开始之前,就从s这个位置开始。

 

void kmp_match(char *text, char *p, int *next)
{
	int 	m, n, s, q;

	m = strlen(p);
	n = strlen(text);
	q = s = 0;	/* q表示上一次迭代匹配了多少个字符,
				   s表示这次迭代从text的哪个字符开始比较 */ 
	while (s < n) {
		for (q = next[q]; q < m && p[q] == text[s]; q++, s++);
		if (q == 0) s++;
		else if (q == m) {
			printf("pattern occurs with shift %d\n", s-m);
		}
	}
}
 

四 测试

 

int main()
{
	int 	next[101], n;
	char	*p = "ca";
	char	*text = "cacca";

	compute_prefix(next, p);
	kmp_match(text, p, next);

	return 0;
}
分享到:
评论

相关推荐

    简单kmp算法实现

    简单的kmp算法实现,代码结构十分清晰,适合初学者理解kmp算法

    KMP算法与trie搜索树实现

    KMP算法与trie树算法实现,以前觉得很不好理解,现在学习了正则表达式、NFA、DFA相关理论,并做了一些实践后,发现好理解多了。 shoulea 16:50 2011-5-20

    C++字符串匹配算法理解(从BF算法到KMP算法)

    字符串匹配算法理解(从BF算法到KMP算法) 暴风(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符...

    KMP算法实现模板(c++版)ACM算法

    acm算法模板之kmp模板,对关键代码做了注释,帮助小白理解

    KMP算法的实现

    《数据结构》课程实验,KMP算法是D.E.Knuth与V.R.Pratt和J.H.Morris同时发现的,此算法可以在O(n+m)的试卷数量级上完成串的模式匹配操作。

    java实现KMP算法

    java实现KMP算法,代码非常简单,容易理解。

    详解小白之KMP算法及python实现

    在看子串匹配问题的时候,书上的关于KMP的算法的介绍总是理解不了。看了一遍代码总是很快的忘掉,后来决定好好分解一下KMP算法,算是给自己加深印象。感兴趣的朋友跟随小编一起看看吧

    kmp.rar_KMP算法

    kmp算法的简单实现,应该对理解算法比较有帮助

    KMP-fail.rar_kmp fail_kmp的fail_kmp算法fail数组_kmp算法求fail

    kmp算法中得next数组也叫fail数组的计算很难理解且代码也不容易实现,本代码就是计算fail数组的源代码

    KMP实现串定位精讲

    看了好久终于把KMP算法实现串定位搞懂了,特写了一份比较通俗易懂的文稿,让你快速理解KMP的精髓所在。

    C语言中实现KMP算法的实例讲解

    一般的算法为什么这么低效呢?那是因为主串指针回溯情况过多: ...朴素的模式匹配算法比较容易理解,其实现如下  int Index(char s[], char p[], int pos) { int i, j, slen, plen; i = pos; j = 0; slen =

    字符串 模式匹配 kmp算法 java实现

    这是个比较难理解的算法,虽然代码就那么几行,但真正理解清楚还是要会时间的。

    kmp_char.rar_kmp_char

    KMP算法的C++实现完整代码。可以帮助初学者理解KMP算法的原理。

    十三个常用算法

    五、红黑树算法的实现与剖析 五(续)、教你透彻了解红黑树 六、教你从头到尾彻底理解KMP 算法 七、遗传算法 透析GA 本质 八、再谈启发式搜索算法 九、图像特征提取与匹配之SIFT 算法 九(续)、sift 算法的编译与...

    模式匹配算法设计

    理解模式匹配的含义,掌握简单匹配算法及模式匹配KMP算法 思想,实现(1)编程动态实现简单模式匹配算法及模式匹配KMP算(2)根据给定的主串与模式串,给出根据两种匹配算法进行匹配的各趟匹配结果; 应用例子: ...

    十五个经典算法研究与总结、目录+索引(定稿版)

    五(续)、红黑树算法的实现与剖析 六、教你初步了解KMP算法、updated (KMP算法系列三篇文章) 六(续)、从KMP算法一步一步谈到BM算法 六(三续)、KMP算法之总结篇(必懂KMP) 七、遗传算法 透析GA本质 八、再...

    快速模式匹配算法(KMP)的深入理解

    恐怕现在用过电脑的人,一定都知道大部分带文本编辑功能的软件都有一个快捷键ctrl+f 吧(比如word)。这个功能主要来完成“查找”,“替换”和...这就是模式匹配的定义啦,下面来看看怎么实现模式匹配算法吧。2.朴

    算法文档,来看看吧

    [原网页] 六之再续:KMP算法之总结篇(12.09修订,必懂KMP) [原网页] Nginx源码剖析之内存池,与内存管理 [原网页] 程序员编程艺术第一~二十二章集锦与总结(教你如何编程) [原网页] 从Trie树(字典树)谈到...

    数据结构实验报告 字符串.doc

    理解Brute-Force 和KMP模式匹配算法,理解next数组在KMP算法中的作用。 使用数组实现字符串类的各种操作算法,数组容量不足时扩充容量的方法。 二、实验题目 ①比较this与obj引用的串是否相等 ②返回将this中所有...

Global site tag (gtag.js) - Google Analytics