【我的第一篇博客】KMP算法(超详细)

本文介绍了暴力(BF)算法和KMP算法在字符串匹配中的应用。BF算法是一种基本的匹配方式,而KMP算法通过next数组避免了不必要的回溯,提高了匹配效率。详细阐述了KMP算法的思想,包括next数组的计算方法,并给出了C语言实现的代码示例。
摘要由CSDN通过智能技术生成

1. BF

BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

这里是BF是很常见的手法,根据题目要求大家一般自己就可以实现,难度不大,所以我们也不过多解释,这里可以说一下算法思想

当目标串S=“aaaaab”,模式串T=“aaab”时,S的长度为6,T的长度为4。

S与T会开始匹配,i=1,j=1时,a与a相等,那么++i,++j。当i与j加到4时,不相等了,就让i回溯到i=2,j=1时,又开始一轮配对,直到完成。
这里是引用
在这里插入图片描述

2. KMP

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) 。

2.1 算法思想

KMP当中的i不用回溯,j也不用从头开始。例如目标串S=“ababcabcacbab”,模式串T=“abcac”。

可以显然看出比较到第3个字符时不一样,
在这里插入图片描述
这时候我们不让i回溯,也即是i不变,就从第3个字符开始比较,S中匹配到第7位字符时匹配不成功,这时i仍然不回溯,
在这里插入图片描述
而在T中,j有时不必回到头上,因为当前比较的字符c前面的字符a是已经比较过的,而且与第一个字符又相同,那么相当于第一个字符已经比较过了,所以这时我们的j可以从第2个字符开始匹配。
在这里插入图片描述
在这里插入图片描述

通过“i的不回溯和j的不一定是从第一个元素开始匹配”,KMP算法才能够提速,才能够让时间复杂度为O(n+m)。
小伙伴们可能开始疑惑了,j到底要怎么去确定它的位置呢?
那么就请跟谁我的脚步,去追寻KMP算法的核心吧!

2.2 next——j的下一个位置

j到底要回到什么位置呢?从第几个字符开始呢?这样的两个问题我们称之为j的下一个位置,用next[j]这样一个数组来表示,那next[j]有是怎么算的呢?

2.2.1 next的思想

以下内容只与模式串有关系

如图所示:
当j=1时,next[j]=0;当j!=1时,我们首先看第一种,第一个意思是在模式串中当从头开始到k-1的位置上的字符与j前面的k-1个字符匹配时(为同一个字母),k的值不止一个,所以我们就让next[j]=max(k);其他情况就让next[j]=1。

在这里插入图片描述

有的小伙伴又疑问了,为什么有这样一个取值情况呢?其实这个为什么不需要我们掌握,研究起来也很麻烦,如果有小伙伴想得通的话,欢迎评论区留言,这里我们就不做过多解释。

还有的小伙伴还是没看懂我写的,不要着急,下面我们来看例题,看完之后就把相对于抽象的东西具体化啦!

2.2.2 例题

这里我们先介绍一下串的前缀后缀
如果一个字符串是abcd,那么它的前缀有a、ab、abc,后缀有d、cd、bcd,前缀后缀都不包括其本身。
介绍这个呢是因为你可以看到p1…pk-1就是字符串的前缀,而pj-k+1…pj就是字符串的后缀。
好了,下面是我们的模式串。

在这里插入图片描述

我们很容易知道 当j=1时,next[j]=0;
那么当j=2时,我们看到字符b前面的字符a,a既不是前缀也不是后缀,所以属于其他情况next[j]=1;
当j=3时,我们看到字符前面的字符串ab,a!=b,即前缀!=后缀,所以属于其他情况,那么next[j]=1;
当j=4,a前面的字符串abc,a!=c、ab!=bc,即前缀!=后缀,属于其他情况,next[j]=1;

此时如图在这里插入图片描述
当j=5时,注意,5位置对应的字符a前面的字符串为“abca”,有a=a,但ab!=ca、abc!=bca,所以只有一个字符a相等,也就是说有k-1个字符是一样的,故 k-1=1,即k=2,此时是唯一的k,也是最大的k,所以next[j]=max(k)=2;
在这里插入图片描述
j=6时,字符串 abcaa中,a=a、ab!=aa、abc!=caa、abca!=bcaa,所以k-1=1,即k=2,next[j]=max(k)=2;
在这里插入图片描述
j=7,字符串 abcaab中,a!=b、ab=ab、abc!=aab、abca!=caab、abcaa!=bcaab,此时有2个字符相等,所以k-1=2,即k=3,则next[j]=max(k)=3;
在这里插入图片描述
再看一下这个图
在这里插入图片描述j=8,容易发现字符串abcaabb中前缀与后缀无一相等,此时j!=1,所以属于其他,则next[j]=1;在这里插入图片描述
j=9,情况与j=8一样,next[j]=1;
j=10,字符串abcaabbca中,a=a,所以k=2,next[j]=2;
j=11,字符串abcaabbcab中,ab=ab,所以k-1=2,next[j]=max(k)=3;
j=12,字符串abcaabbcabc中,abc=abc,所以k-1=3,next[j]=max(k)=4;

最终情况如下图
在这里插入图片描述

看到这里相信大部分小伙伴都理解到了吧!
那么我们的代码如何实现呢?

2.3 代码实现——c语言

用的c语言编写,也没有分文件编写,代码不多,希望大家多多支持!

#incude<stdio.h>

int length(char *a)   
{
	char *b=a;
	int i=1;
	while(b[i]!=NULL)
	{
		++i;
	}
	return i;
 } 
 
 int Index_KMP()
 {
 	char s[]="ababcabcacbab";
 	char t[]="abcac";
 	int a=1,b=0;   
 	int next[20];
	next[1]=0 ; //next数组是从下标为1开始的 
	
 	while(a<length(t))      //实现next的变化过程 
 	{
	 	if(b==0||t[a]==t[b]) 
	 	{                    
	 		++a;++b;
	 		next[a]=b;
		 } 	
		 else 
		    b=next[b];	
	 }
	 
 	int i=1,j=1;
 	while(i<length(s)&&j<length(t))
 	{
 		if(j==0||s[i]==t[j]) //恰好完全匹配 
 		{
 			++i;++j; 			
		 }
		 else            
		    j=next[j];//i不变,j后退
	 }

	 if(j>=length(t))       
	     return i-length(t);//匹配成功,返回结束位置 
	 else 
	     return -1;  //返回不匹配的标志 
	    	
 }
 int main()
 {
 	printf("%d",Index_KMP());
 	return 0;
 }
   

以上是基础版的KMP,如果当我们的模式串是“ aaaaaab”这样的,其实我们还有更简便的方法,大家可以自己去思考一下。

有什么不正确的地方欢迎一起讨论指正,hhhh
冲冲冲

请添加图片描述

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
KMP算法是一种字符串匹配算法,它的全称是Knuth-Morris-Pratt算法,由Donald Knuth、Vaughan Pratt和James H. Morris三人于1977年联合发表。KMP算法的核心思想是利用已知信息尽可能地减少匹配的次数。 KMP算法的实现需要两个步骤:预处理和匹配。预处理阶段是为了计算出模式串中每个位置的最长公共前后缀长度,匹配阶段则是利用预处理结果进行匹配。 具体来说,预处理阶段需要计算出模式串中每个位置的最长公共前后缀长度,这个长度可以用一个数组next来存储。next[i]表示模式串中以i结尾的子串的最长公共前后缀长度。计算next数组的方法是从前往后依次计算,假设已经计算出了next到next[i-1],现在要计算next[i],则需要比较模式串中以i-1结尾的子串和以0结尾的子串、以1结尾的子串……以next[i-1]结尾的子串,找到最长的公共前后缀即可。 匹配阶段则是利用预处理结果进行匹配。具体来说,假设现在要在文本串中查找模式串,首先将模式串和文本串的第一个字符进行比较,如果相等,则继续比较下一个字符,否则需要根据next数组来移动模式串的位置。具体来说,假设当前模式串的位置是j,文本串的位置是i,如果模式串中第j个字符和文本串中第i个字符不相等,则需要将模式串向右移动j-next[j]个位置,这样可以保证模式串中前next[j]个字符和文本串中前i-(j-next[j])-1个字符是相等的。如果模式串已经移动到了最后一个字符,说明匹配成功,否则继续比较下一个字符。 KMP算法的时间复杂度是O(m+n),其中m和n分别是模式串和文本串的长度。KMP算法的优点是可以在O(m+n)的时间内完成匹配,而且不需要回溯文本串中已经匹配过的字符。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值