【C语言】通俗易懂的KMP算法

一、认识KMP算法

  KMP算法是一种改进的字符串匹配算法,核心是利用匹配失败后通过一个next数组,使得减少匹配串和主串的匹配次数达到提高效率。

  KMP算法和KF算法(暴力求解的算法)不同的是,主串的 i 不会回退,而匹配串的 j 会有规律的回退到不同位置。

二、KMP算法实现思路

让我们来一个例子
我们将下面这串字符当作主串
在这里插入图片描述

这串字符当作匹配串
在这里插入图片描述

1、为什么不用回退 i

我们给它们标上坐标号
通过下面我们可以知道, i 和 j 在5号位置匹配失败,这个时候如果回退 i 到1号位到b,还是不匹配成功的,所以我们可以尝试用一些方法简化一下。
在这里插入图片描述
我们观察到,在匹配失败的位置前,都是匹配成功了的,再观察到,在前面的ab出现了两组,那么 j 如果在5号位匹配失败,就可以在2号位再试试匹配,如果不成功那么就从0号位开始。
在这里插入图片描述
那么如何有规律的确定 j 回退的位置呢?

2、求next数组

KMP中的next数组:next[ j ] = k,在next数组里每个 j 位置对应着一个k值,这个k值就是 j 需要移动的坐标值。
k的规则:

  1. 找到匹配成功的部分子串,一个从0坐标开始,一个在j-1坐标结尾。
  2. 不管是什么字符串,next[0]=-1,next[1]=0。


比如:
在这里插入图片描述
next[5]=2,假设字符串数组名是p,j=5,从坐标0开始p[0]->p[1]==p[3]->p[4],ab长度是2,所以k=2。
next[4]=1,j=4,从坐标0开始p[0]==p[3] (j-1),a长度是1,所以k=1。

所以数组p的next数组为
在这里插入图片描述
下面有两个关于next数组的练习题:
在这里插入图片描述
答案:
在这里插入图片描述

3、确定 j 回退的位置

前面我们通过人为推断确定了next各个位置的值,但是在程序中我们如果已知0和1号位置的k值(-1和0),那么该如何确定其它位置的值。
我们先通过人为推断的结果找找规律。

我们知道了next数组中的 k 是怎么来的。
接下来让我们看看其中的推断。
用这个字符串数组举例
在这里插入图片描述
在这里插入图片描述

4、总结思路

  1. 通过p[ j ]是否等于p[ k ],确定next数组各个位置的k值。
  2. 再进行正常匹配, 如果匹配不成功,就将 j 移动到next数组对应到的位置。
  3. 最后通过返回主串 i 的位置和匹配串 j 的位置差,确定字符串从哪个位置开始的匹配。

三、KMP算法实现代码

//通过sub[i]与sub[k]关系确定next中各个的值
void GetNext(char* sub, int* next, int len)
{
	next[0] = -1;
	next[1] = 0;
	int i = 1;//从已知的1位置开始
	int k = 0;//1位置已知的k值

	while (i < len)
	{
		if (k == -1 || sub[i] == sub[k])
		{
			next[i + 1] = k + 1; //推论
			k++;
			i++;
		}
		else
		{
			k = next[k]; //不一样就回退
		}
	}
}

int KMP(char* str, char* sub, int pos)
{
	assert(str && sub);
	int lenstr = strlen(str);
	int lensub = strlen(sub);
	if (lenstr == 0 || lensub == 0)
	{
		return -1;
	}
	if (pos >= lenstr)
	{
		return -1;
	}

	int i = pos;
	int j = 0;

	//开辟next数组
	int* next = (int*)malloc(lensub * sizeof(int));
	assert(next);
	
	//获得next数组
	GetNext(sub,next,lensub);
	
	//开始匹配
	while (i < lenstr && j < lensub)
	{
		if (j == -1 || str[i] == sub[j]) //j在一开始匹配不成功就会为-1
		{
			i++;
			j++;
		}
		else
		{
			j = next[j]; //匹配不成功j退回next[j]位置
		}
	}
	if (j >= lensub)
	{
		return i - j;
	}
	else
	{
		return -1;
	}
	free(next);
	next = NULL;
}

int main()
{
	printf("%d\n", KMP("bbcabcsabdbb", "abcsa", 0)); //3
	printf("%d\n", KMP("abbbbc", "bbc", 0)); //3
	return 0;
}

四、总结

本章思路是:介绍KMP算法,理解next数组以及k怎么来的,通过next数组中的结论看到推论,再通过推论反推next数组。
KMP算法主要是在 i 和 j 位置匹配不成功情况下,通过next数组改变j的位置进行重新匹配,然后就是如何确定next数组。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值