栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

文章目录
    • 一、认识KMP算法
    • 二、KMP算法实现思路
      • 1、为什么不用回退 i
      • 2、求next数组
      • 3、确定 j 回退的位置
      • 4、总结思路
    • 三、KMP算法实现代码
    • 四、总结

一、认识KMP算法

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

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

二、KMP算法实现思路

让我们来一个例子
我们将下面这串字符当作主串

这串字符当作匹配串

1、为什么不用回退 i

我们给它们标上坐标号
通过下面我们可以知道, i 和 j 在5号位置匹配失败,这个时候如果回退 i 到1号位到b,还是不匹配成功的,所以我们可以尝试用一些方法简化一下。

我们观察到,在匹配失败的位置前,都是匹配成功了的,再观察到,在前面的ab出现了两组,那么如果在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[4]->p[5],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("%dn", KMP("bbcabcsabdbb", "abcsa", 0)); //3
	printf("%dn", KMP("abbbbc", "bbc", 0)); //3
	return 0;
}

四、总结

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1013358.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号