strstr的模拟实现以及KMP算法对其优化

strstr函数就是一个在主串中寻找是否含有某个子串的
返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。
匹配过程不包括终止空字符,但它就此停止

我们来看一下思路吧
在这里插入图片描述
接下来看代码:

char* my_strstr(const char* str1, const char* str2)//加const为了不让值改变
{	
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* cur = str1;
	while (*cur)
	{
		s1 = cur;
		s2 = str2;
		while (*s1&&*s2&&(*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return (char*)cur;

			cur++;
		
	}
	return NULL;
}

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

那我们该怎么优化呢?
于是就有了

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

K算法的核心就是next数组,那么next数组该怎么理解呢?
我们先来看看KPM算法执行流程吧!
KPM相对于BF算法来说,他并不是完全回溯到子串的第一个元素,而是记录刚刚子串和主串相同部分的区域,在这区域有相同的部分,那直接用子串的第一部分移动到主串的第二部分,然后开开始向后找,这样说可能不太理解,那我用图解来解释一下!!
在这里插入图片描述
在这里插入图片描述

当然,在有的情况根本就找不到相同的部分啊,那我们该怎么搞呢?
其实这里得引用一个next数组,用next数组来确定回溯的位置,那么next的原理是什么呢?
我们刚刚说了在匹配的字符串中,找到相同的部分,然后就可以直接唯一这个字符串
我们现在就得研究这个next该怎么写出来!
我们看图:
在这里插入图片描述

规定第一个元素为-1,第二个为0,然后在第三个开始确定重叠长度,但是每次都不算自己本身的,就这样一个一个找,我们看最后一个元素A,它前面重叠的长度有3个,这些值我们都得传到next数组里,但是我们怎么来确定next的所有值呢?
我们来推导看看
假设第一部分重叠最后一个元素下标为k-1,第二部分的下标那就是i-1了(i是要确定的那个数组元素下标)
因为第一部分和第二部分长度相同,那么就是next[0]…next[k-1]==next[i-k]…next[i-1]
如果next[k]==next[i],那么next[0]…next[k-1]next[k]==next[i-k]…next[i-1]next[i],也就是是长度在原来基础上+1,那么next[i+1]=k+1
但是如果不相等呢?
那么k就得回溯,直到next[k]==next[i],这有进入上一个循环,但是我们怎么设计这个k的回溯呢???这是一个重点,也是一个难点!!
我们用k=next[k]来回溯,为什么呢?
我用图解一下

假设next[15]=7
在这里插入图片描述

如果next[15]==next[7],那么next[16]=7+1=8
如果不相等呢??那我们就得找到一个k值让next[k]==next[i-1]
我们假设next[7]=3

在这里插入图片描述
因为5=6,1=2,3=4所以1=2=3=4 因为这个next[15]!=next[7],现在k回溯到next[k]的位置,如果相等了就可以继续循环了 ,如果还不等,那在依次重复上面的操作,这有点递归那个味了,哈哈,现在知道为什么k=next[k]了嘛,其实这个next[k]不就是找上一个重复部分的位置的嘛,上面那个图,看找到了3这个位置,如果next[15]= =next[3]了的话,那next[15]= =3+1=4,如果不想相等就继续,但是当k=next[k]一直找到第一个元素了,我们规定了next[0]=-1,那么如果一直找到第一个元素了,那么next[i]=0了;所以我们写代码的时候在判断next[i-1] = = next[k]的时候得改成k==-1||next[i-1]==next[k]
那么我现在附上代码:

void Getnext(char* str2, int* next,int lenstr2)
{
	next[0] = -1;
	next[1] = 0;
	int i = 2;
	int k = 0;
	while (i < lenstr2)
	{
		if (k == -1 || (next[i - 1] == next[k]))
		{
			next[i] = k + 1;
			i++;
			k++;
		}
		else
			k = next[k];//回溯
	}

	
 }
int KMP(char* str1, char* str2,int pos)
{
	assert(str1 && str2);
	int lenstr1 = strlen(str1);
	int lenstr2 = strlen(str2);
	if (lenstr1 == 0 || lenstr2 == 0) return -1;
	if (pos<0 || pos>lenstr1) return -1;
	int i = pos;
	int j = 0;
	int* next=(int*)malloc(sizeof(int) * lenstr2);
	assert(next != NULL);
	Getnext(str2, next,lenstr2);
	while (i < lenstr1&&j<lenstr2)
	{
		if (j==-1||str1[i] == str2[j])
		{
			
			i++;
			j++;
		}
		else
			j = next[j]; //平移子串,从新判断
		if (j >= lenstr2)
			return i - j;
	}
	return -1;

}
int main()
{
	char arr1[20] = "aabbccddbbabadba";
	char arr2[3] = "cd";
	int str = KMP(arr1, arr2,0);
	printf("%d", str);
	return 0;
}
  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

相知-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值