一起来学数据结构——KMP算法

一起来学数据结构——KMP算法

首先我们先来看一下BF算法。

BF算法

char* my_strstr(const char* arr1, const char* arr2)
{
	char* str1 = (char*)arr1;
	char* str2 = (char*)arr2;
	char* cp = str1;
	while (*cp)
	{
		str1 = cp;
		str2 = arr2;
		while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
		{
			str1++;
			str2++;
		}
		if (*str2 == '\0')//如果将目标元素全部遍历完成后
			return cp;//就返回找到的
		cp++;
	}
	return NULL;//返回NULL
}

KMP算法

思想

上面的BF算法虽然是可以解决问题的,但是每次都从子串的头部开始重新调整。对一般的字符串是没有太大的问题的,但是对于那些子串中有重复的字符串的子串来说,再从头开始寻找就有一些多次一举了。

image-20211020102622927

在这种情况下,d和f明显就是不相等了,按照原来的BF算法,就得让i到b,j到a重新开始比较,但是都不相同。

但是我们的KMP算法就比较的简便了,直接找到相同的部分,然后从那往后开始比较。

要找和紧挨的子串相同的

KMP算法就是充分利用了子串中的重复字符串的特性,使得子串的字符和主串的字符不相同的时候,可以不用回到子串的0处开始寻找,

而是找从下标0开始,以j-1结尾,能否找到两个相同的字符串

使用next数组

将子串中每一个元素和主串不相同时跳转的下标做成一个数组。下标的大小就是相同字符串的长度,并规定第一个字符跳转的下标是-1,第二个字符跳转的下标是0

image-20211020103730834

image-20211020110556159

深度剖析next

现在我们要研究的问题是:

我们知道了j的某个字符要跳转到哪,我们就来推测一下j+1跳转到哪?

第一种情况p[j]==p[k]

image-20211020142152627

第二种情况p[j]!=p[k]

image-20211020182547936

如果p[j]和p[k]不相同的话,k就要往左看,是否相同了,

让k=next[k],在来判断是否相同

代码实现

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
void getnext(int* next, const char* sub)
{
	int lensub = strlen(sub);
	next[0] = -1;
	next[1] = 0;
	int i = 2;
	int k = next[i - 1];//k的初始值就是下标为1的值
	while (i < lensub)
	{
		if (k==-1||sub[i - 1] == sub[k])
		{
			next[i] = ++k;//既然相等,k向后挪一个,赋予next数组
			i++;//i也要向后
		}
		else
		{
			k = next[k];
		}
	}
}
const char* KMP(const char* str, const char* sub)
{
	//判断是否传来空指针
	assert(str && sub);
	int lenstr = strlen(str);
	int lensub = strlen(sub);
    //判断字符串的长度是否为0
	assert(lenstr && lensub);
    //创建next数组
	int* next = (int*)malloc(sizeof(int) * lensub);
	assert(next);
    //赋值next数组
	getnext(next, sub);
	int i = 0, j = 0;
    //都没有到达两个数组的末尾的时候
	while (i < lenstr && j < lensub)
	{
		if (j == -1 || str[i] == sub[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];
		}
	}
	free(next);
    //如果成功走到了子串末尾,就返回
	if (j == lensub)
		return str+i - j;
	return NULL;
	
}
int main()
{
	const char* str = "ababcabcdabcde";
	const char* sub = "abcdf";
	printf("%s\n", KMP(str, sub));
	return 0;
}

对next数组的优化nextval

有时候字符串更加特殊的时候,我们就可以使用优化过的nextval数组,使得更加的简便。

比如下面的子串:

image-20211020190404927

比如在最后一个a的位置和主串的元素不一样了,所以回退到倒数第二个位置去,但是倒数第二个位置的元素还是a,还是和主串不一样。

这样得一直回退到第一个元素。

所以,对于这种回退之后的元素和本元素一样的问题,我们就可以考虑对next数组进行优化。

优化的原则是这样的:

  1. 如果回退的位置的字符和当前位置一样,nextval数组就写那个位置的nextval
  2. 如果回退的位置的字符和当前位置的不一样,nextval数组就还是写那个位置的next
image-20211020191441527

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值