26-BF算法(朴素查找算法)和KMP算法

本文详细介绍了BF(朴素)算法和KMP算法的基本思想、实现过程及区别。BF算法在匹配失败时,i回退到起始位置的下一个位置,j回退到0;而KMP算法的i不需要回退,j退到next数组指示的位置。通过next数组,KMP算法提高了匹配效率,时间复杂度为O(n+m)。同时,文章提供了两种算法的C语言实现,并给出了使用示例。
摘要由CSDN通过智能技术生成

1.BF算法是笔试面试题,一定要能够写出

2.BMP算法是面试题,一定要可以自己描述出算法思想

3.BF算法的i每次回退到刚才位置的下一个位置(即i-j+1),j每次回退到0

4.KMP算法的i不回退(这是重点),j退到该退的位置k

5.next数组的长度和子串的长度一样,保存的是子串失配时每一个位置对应的k值,即要回退到的地方的下标

6.主串(s) 子串§

BF算法(笔试面试重点题)

朴素(简单)查找算法:失配时,i回退到刚才起始位置的下一个位置,j回退到0

笔试面试重点题,时间复杂度为O(n*m),空间杂度为O(1)

代码

int BF(const char* str, const char* sub, int pos)//笔试重点题  O(n*m),O(1)
{
	//参数判断
	assert(str != NULL && sub != NULL);
	if(str == NULL || sub == NULL)
		return -1;
		
	int lenstr = strlen(str);//主串长度
	int lensub = strlen(sub);//子串长度
	if (pos < 0 || pos >= lenstr)
		return -1;
	int i = pos;//主串下标
	int j = 0;//子串下标
	while (i < lenstr && j < lensub)//没有比对完
	{
		if (str[i] == sub[j])//匹配成功,一起往前走
		{
			i++;
			j++;
		}
		else//匹配失败(失配)
		{
			i = i - j + 1;//i回退到刚才起始位置的下一个位置
			j = 0;//j回退到0
		}
	}
	if (j >= lensub)//子串完成,说明找到了,只能通过子串来判断是否完成
		return i - j;//找到了,返回第一个匹配成功的下标
	else
		return -1;//失配了,返回-1
}

int main()
{
	printf("%d\n",BF("ababcabcdabcde","abcd",0));//5
	printf("%d\n",BF("ababcabcdabcde","abcd",6));//9
	printf("%d\n",BF("ababcabcdabcde","abcd",10));//-1

	return 0;
}

KMP算法(不笔试只面试)

KMP查找算法:特点,i不需要回退,j退到该退的位置k

笔试面试重点题,时间复杂度为O(n+m),空间复杂度为O(m)(m长度的next数组)

代码

static void GetNext(const char* sub, int* next)
{
	//参数判断
	assert(sub != NULL || next != NULL);
	if(sub == NULL || next == NULL)
		return ;
	
	int lensub = strlen(sub);
	if (lensub <= 1)
	{
		next[0] = -1;//必须有,比如"abc","c"这个例子
		return;
	}
	next[0] = -1;
	next[1] = 0;
	int j = 1;
	int k = 0;
	while (j + 1 < lensub)//根据next[j]求next[j+1]
	{
		if (k==-1 || sub[k] == sub[j])//如果下一个字符相等
		{
			next[++j] = ++k;
			/*next[j + 1] = k + 1;
			++j;
			++k;*/
		}
		else
		{
			k = next[k];
		}
	}
}
//KMP算法,特点是i不需要回退,其时间复杂度为O(n+m).不笔试只面试
int KMP(const char* str, const char* sub, int pos)//面试重点题,时间复杂度O(n+m),空间复杂度O(1)
{
	assert(str != NULL && sub != NULL);
	if(str == NULL || sub == NULL)
	{
		return -1;
	}
	
	int lenstr = strlen(str);//主串长度
	int lensub = strlen(sub);//子串长度
	if (pos < 0 || pos >= lenstr)
		return -1;
	int i = pos;//主串下标
	int j = 0;//子串下标
	int* next = (int *)malloc(lensub*sizeof(int));//创建一个和字串长度一样的动态数组,用来保存字符串失配的所有位置对应的k值
	GetNext(sub,next);//获得每一个位置的k值
	assert(next != NULL);
	while (i < lenstr && j < lensub)
	{
		if (j==-1 || str[i] == sub[j])//匹配成功
		{
			i++;
			j++;
		}
		else//匹配失败(失配)和BF算法不同
		{
			//i不回退,j退到该退的位置k
			j = next[j];//j = k;
		}
	}
	free(next);
	if (j >= lensub)//子串完成,说明找到了,只能通过子串来判断是否完成
		return i - j;//找到了,返回第一个匹配成功的下标
	else
		return -1;//失配了,返回-1
}

int main()
{
	printf("%d\n",KMP("ababcabcdabcde","abcd",0));//5
	printf("%d\n",KMP("ababcabcdabcde","abcd",6));//9
	printf("%d\n",KMP("ababcabcdabcde","abcd",10));//-1
	printf("%d\n",KMP("abc","c",0));//2

	return 0;
}

在这里插入图片描述

公式(1) 黑线左边的一大块相等,所以得出公式1,x是未知数,结果x=i-j

公式(2) 图中两个画黑线的长度相等,所以得出公式2,x是未知数,结果x=i-k

公式(3) 主串黑线的长度和子串红线的长度相等,所以得出公式3,x是未知数,结果x=j-k;

公式(2)和公式(3)联立得:

P0…Pk-1 = Pj-k…Pj-1

所以得出了一个结论:k回退的位置和主串没有关系,只与子串有关系

在匹配成功的子串中查找两个最长且相等的真子串(可重叠),这两个真子串要求如下:

1.一个以子串的开头作为开头

2.另一个以匹配成功的最后一个字符作为结尾,这个真子串的长度就是要求的k(即j回退的位置)

![在这里插入图片描述](https://img-blog.csdnimg.cn/64c576dbe039442cb1058af72452d35b.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAc3BfMTMyMzA0MDk2MzY=,size_20,color_FFFFFF,t_70,g_se,x_16

在匹配成功的子串中查找两个最长且相等的真子串(可重叠),这两个真子串要求如下:

1.一个以子串的开头作为开头

2.另一个以匹配成功的最后一个字符作为结尾,这个真子串的长度就是要求的k(即j回退的位置)

3.P0…Pk-1 = Pj - k…Pj-1

BF算法和KMP算法的区别

1.BF面试笔试都会出现,而KMP只会出现在面试中

2.BF的特点是:失配时,i回退到刚才起始位置的下一个位置,j回退到0;KMP的特点是:i不需要回退,j退到该退的位置k

3.KMP比BF多修改的地方:

①创建动态内存next数组,用来保存子串失配时所有位置对应的k值

int *next = (int *)malloc(sizeof(int) * strlen(str));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值