`
kenby
  • 浏览: 725724 次
  • 性别: 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算法的原理和C语言实现,我们可以更好地理解模式匹配算法,并将其应用于实践中。

    KMP算法C++源码实现(有工程,可编译)

    在C++实现KMP算法时,首先需要构建一个部分匹配表(也称为失配表),这个表记录了当主串与模式串不匹配时,模式串应该移动的步数,以避免重复比较已经比较过的字符。这部分通常涉及两个关键步骤: 1. **构造部分...

    KMP算法的C语言实现

    在C语言中实现KMP算法,我们需要理解以下几个关键概念: 1. **前缀表(部分匹配表)**:这是KMP算法的核心,用于存储模式串(待查找的子串)中的部分匹配信息。前缀表记录了模式串中每个字符之前最长的公共前后缀...

    KMP 算法理解

    《KMP算法理解》 KMP(Knuth-Morris-Pratt)算法是一种在文本串中查找模式串的高效算法,由D.E.Knuth、V.R.Morris和J.H.Pratt于1970年提出。这个算法的核心思想是避免在匹配过程中出现部分匹配的情况,从而提高搜索...

    数据结果 kmp算法实验报告

    1. **比较Brute-Force算法和KMP算法的比较次数**:编写程序实现这两种算法,并统计它们在处理相同输入时的比较次数,从而直观地比较两者的效率差异。 2. **实现串的替换功能**:编写函数`Replace(S, start, T, V)`,...

    KMP算法的图形界面实现

    通过这样的图形界面,用户不仅能够直观地理解KMP算法的工作机制,还能通过实际操作加深对算法的理解。这对于学习者来说是一种非常有益的教学方式,同时也为开发者提供了一个交互式的调试工具。 在提供的文件...

    KMP算法(C++实现)

    KMP算法是一种高效的字符串匹配算法,它由Donald Knuth、Vaughan Pratt和James H. Morris共同发明,因此得名。KMP算法的核心思想是在不匹配时,能够利用已经...正确理解next数组的含义和计算方法是掌握KMP算法的关键。

    传统KMP算法与改进KMP算法的对比

    在实现KMP算法时,需要注意内存管理、循环优化和代码结构清晰等问题。源码软件应具备良好的可读性和可维护性,同时,通过合理的数据结构和算法设计,可以进一步提高程序的运行效率。 在进行算法性能对比时,通常会...

    KMP算法与trie搜索树实现

    在理解了KMP算法和Trie搜索树的基本原理之后,结合这些源代码和可执行文件,你可以进一步研究它们的实现细节,包括内存管理、递归或迭代策略、错误处理等方面,以提升自己的编程技能和对字符串处理的理解。...

    kmp算法,C语言实现

    KMP(Knuth-Morris-Pratt)...对于初学者而言,理解并实现KMP算法能帮助他们掌握动态规划的思想,提高解决实际问题的能力。通过阅读和分析给出的C语言代码,初学者可以深入理解KMP算法的细节,并在实际项目中灵活运用。

    KMP算法的实现

    在“KMP算法的实现”中,我们的目标是找到文本中的最长公共子串,即在文本中与模式匹配的最长连续子串。 KMP算法的核心在于预处理模式串,生成部分匹配表(也称为失配表)。这个表记录了当模式串中出现不匹配时,...

    KMP算法C语言实现

    在C语言中实现KMP算法,可以有效地处理字符串匹配问题,避免了朴素算法在匹配过程中频繁的回溯。 KMP算法的核心思想是利用已知的前缀和后缀的关系来提高匹配效率。它构建了一个部分匹配表(也称为失败函数或跳跃表...

    BF算法和KMP算法

    BF 算法和 KMP 算法在字符串匹配中的应用 ...BF 算法和 KMP 算法都是字符串匹配中常用的算法,但它们的时间复杂度和实现难度不同。在实际应用中,需要根据实际情况选择合适的算法,以提高matching效率。

    算法分析与设计KMP算法字符串改进

    《算法分析与设计:KMP算法的字符串匹配优化》 KMP算法,全称为Knuth-Morris-Pratt算法,是计算机科学中一种用于字符串匹配的高效算法。它避免了在进行比较时出现的不必要的回溯,从而显著提高了匹配效率。在给定的...

    用KMP算法实现的文本检索

    在实现KMP算法时,我们需要使用数组或链表等数据结构来存储模式串和部分匹配表,同时还需要理解栈和队列等其他数据结构,以便在处理主串时能灵活地进行后退和前进操作。 MFC是微软提供的一套面向对象的类库,用于...

    简单kmp算法实现

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

    数据结构课程设计实验报告-KMP算法的实现

    通过KMP算法的实现,不仅提升了对数据结构的理解,也加深了对C语言的掌握。此外,算法设计能力的提升,对于解决实际问题,尤其是涉及字符串处理的场景,有着重要的意义。这份实验报告充分展示了理论知识与实践操作的...

    KMP算法实现(C语言)

    在C语言中实现KMP算法,我们需要理解其核心思想和步骤。 1. **KMP算法的核心思想**: - KMP算法的关键在于构造一个部分匹配表(也叫失配表),用于记录当主串与模式串比较时,如果发生不匹配,模式串应如何移动以...

    易语言KMP算法模块

    易语言KMP(Knuth-Morris-Pratt)算法模块是一个专门为易语言设计的文本处理工具,用于在字符串中...同时,KMP算法也是字符串匹配领域的一个经典算法,学习它的实现原理也有助于深入理解计算机科学中的动态规划思想。

Global site tag (gtag.js) - Google Analytics