KMP算法图解解析(C语言)

文章目录

  • 一.引言
  • 二.KMP算法解析
  • 三.代码实现
    • 1.对目标子串的处理(创建next数组)
    • 二.寻找子串的KMP算法实现

一.引言

kmp算法是由三位大牛共同研究提出的,全称为Knuth-Morris-Pratt算法,简写为KMP算法。

kmp算法用来解决子串的问题,
比如说有一串字符串–abcdefg,求解abc是否为该字符串的子串。
一般情况下,我们可能会想到使用暴力遍历的方法,暴力求解,但是这种方法的时间复杂度为O(n*m),也就是说如果主串和子串的长度足够长,那么暴力遍历的方法就会十分低效。
这个时候,KMP算法的优势就出来了,他的时间复杂度只有O(m+n)
接下来,我们详细的介绍这种方法。

二.KMP算法解析

在我们开始的时候,我们先去理解KMP的原理:
如果我们一个字符串为
c d c d e
我们需要求解
c d e是否为子串
请添加图片描述

我们的跳跃遍历是有错误的,有风险的,如果出现目标子串为abac的时候
这个方法就会失败!!!
请添加图片描述

所以KMP算法中有一个next值(前缀函数)帮我们解决了这一个问题
他定义:
一个字符串如果前面部分与后半部分相同那么
这个前半部分(=后半部分)的长度就是next(前缀函数)

例如:
a b a c a b a

-
a0
a b0
a b a1(正着为a,反着也是a)
a b a c0
a b a c a1
a b a c a b2(正反的ab和ab)
a b a c a b a3(同理)

有了这个next的值,那么我们在遇到不匹配时优先考虑回溯,再继续向后遍历,可以省下许多时间减小复杂度。请添加图片描述

回溯的原因就是因为我们的子串如果前缀与后缀相同,可以直接继续遍历减少复杂度

三.代码实现

1.对目标子串的处理(创建next数组)

我们不妨思考一下,如果每次我们遍历的时候都去计算一下我们的目标字串应该回溯的值,那么这回非常复杂,麻烦且没必要。
所以这三位大佬创建了一个数组来存储这个目标子串的每一位的回溯值,KMP算法的精髓就在这里
我们可能会想到用暴力遍历这个子串,来求出他的next数组但是KMP算法有一种更精妙的方法来建立next数组
好了我们来处理吧:
请添加图片描述
代码实现如下:

void setnext(char* p, int* next)
{
	int sz = strlen(p);
	int i = 0, j = 0;
	next[0] = 0;
	for (i = 1; i < sz; i++)//i在后面,j在前面
	{
		while (j > 0 && p[i] != p[j])//当j没回溯到开头或者p[i]!=p[j]的时候
		{
			j = next[j - 1];
		}
		if (p[j] == p[i])
		{
			j++;
		}
		next[i] = j;//因为每一次遍历都会让i向后走一步,所以赋值写在最后
	}
}

二.寻找子串的KMP算法实现

由以上的思路我们合并在一起,就能得到我们的寻找子串的代码:

#include <stdio.h>
#include <string.h>

void setnext(char* p, int* next)
{
	int sz = strlen(p);
	int i = 0, j = 0;
	next[0] = 0;
	for (i = 1; i < sz; i++)//i在后面,j在前面
	{
		while (j > 0 && p[i] != p[j])//当j没回溯到开头或者p[i]!=p[j]的时候
		{
			j = next[j - 1];
		}
		if (p[j] == p[i])
		{
			j++;
		}
		next[i] = j;//因为每一次遍历都会让i向后走一步,所以赋值写在最后
	}
}
int _strstr(char*p,char*dest)
{
	int* next = NULL;
	int sz = strlen(p);
	next = (int*)malloc(sizeof(int) * sz);
	setnext(p, next);
	int m =strlen(dest);
	int j = 0;
	for (int i = 0; i < m; i++)
	{
		while (j > 0 && p[j] != dest[i])
		{
			j = next[j - 1];
		}
		if (dest[i] == p[j])
		{
			j++;
			if (j == sz)
			{
				free(next);
				return 1;
			}
		}
		
	}
	return 0;
}
int main() {
    const char* pattern = "abababb";
	char* p2 = "ababadasdawdasdabababbsabb";
    
    printf("Y or N:%d", _strstr(pattern,p2));

    return 0;
}

新人博主用词不严谨请多多指教。
创作不易,恳请留赞(๑´ڡ๑)(๑´ڡ๑)(๑´ڡ`๑)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值