C语言查找-----------BF算法&&KMP算法

1.问题引入

有一个主字符串,有一个子字符串,要求我们寻找子字符串在主字符串里面开始出现的位置;

2.BF算法

BF算法就是暴力算法,这个做法虽然效率不高,但是按照我们传统的思路依然能够得到结果,接下来我们使用C语言实现这个查找的过程;

#include<stdio.h>
#include<assert.h>
#include<string.h>
//返回字串在主串里面的位置
//没有找到返回-1;
int BF(char* str, char* sub)
{
	assert(str && sub);
	if (str == NULL || sub == NULL)
	{
		return -1;
	}
	int lenstr = strlen(str);
	int lensub = strlen(sub);
	int i = 0;
	int j = 0;
	while (i < lenstr && j < lensub)
	{
		if (str[i] == sub[j])
		{
			i++;
			j++;
		}
		else
		{
			i = i - j + 1;
			j = 0;
		}
	}
	if (j >= lensub)
	{
		return i - j;
	}
	else
	{
		return -1;
	}
}
int main()
{
	printf("%d\n", BF("abcabdefabchd","abch"));
	printf("%d\n", BF("abceabcd", "abcd"));
	printf("%d\n", BF("abcabcabc", "abcdefgh"));
	return 0;
}

这段代码的逻辑是这样的:

(1)我们首先定义一个函数BF,我们这个函数的作用就是寻找子串在主串里面的位置,我们可能会在主串里面找到字串,找到就会返回子串在主串里面的位置下标,如果找不到就会返回-1;

(2)我们以代码主函数里面的第一个作为案例,我们使用*str代表主串,*sub代表子串;

(3)我们首先进行断言,断言的话就要包含对应的头文件,如果子串或者主串是空的,那么我们就直接返回-1,因为这样的话肯定找不到;

(4)我们定义i遍历主串,使用j遍历子串,我们使用strlen求两个字符串的长度(这里也是要包含对应的头文件的,如果i<主串的长度而且j小于子串的长度,说明我们正在进行遍历,我们需要在这样的情况下进行判断;

(5)如果i>=主串的长度,证明主串已经找完了,这个时候就是没有找到返回-1,如果j>=子串的长度,说明我们已经找到了,这个时候就要返回子串在主串的位置下标;

(6)接下来我们需要计算这个下标的变化,以代码主函数里面的第一个作为案例,i指向的是主串的第一个字符,j指向的是字串的第一个字符,第一个都是a,i++,j++,第二个都是b,i++,j++,第三个都是c,i++,j++,第四个就不一样了,这个时候我们需要重新寻找,i和j都要回退,j肯定是回退到0下标,i应该从第二个字符开始,因为我们刚刚是从第一个开始找,找不到,那么这个2下标应该如何表示呢?我们首先要清楚的是i和j的移动的个数是一致的,j已经走了j个,那么i-j就可以表示i开始的位置,但是这个位置找不到,我们就要从他的下一个位置开始找,我们就可以用i-j+1进行表示主串里面下标回退的位置;我们设置一个循环,依次进行,直到主串遍历完成或者子串遍历完成就停止退出循环;

(7)如果是j>=子串长度,说明可以在主串里面找到,我们直接返回i-j就可以找到第一个下标了,这个时候,就不需要加一了,因为i-j+1是找不到的时候j回退的位置;

(8)还有一个我们需要注意的细节就是我们在回退的时候,必须先让i回退,再让j回退,因为j如果回退到0,i-j+1显然就不是我们想逃回退的下标了,我们就是想要利用j的下标计算的,如果先把j置为0,肯定是无法得到我们想要的结果的。

3.KMP算法

我们想要了解KMP算法,就必须知道他和我们普通的暴力算法有什么不同之处,其实KMP算法是三个大佬发现的,KMP分别是这3个大佬名字的第一个字母(我们了解一下就可以了),他和普通算法的不同点就在于,

(1)我们上面介绍的BF算法,如果匹配错误,i返回i-j+1下标,我们的KMP算法是不会回退的;

(2)BF算法的j回退到0下标,这个地方KMP是不会回退到0的,而是回退到一个我们指定的位置,这个回退的指定的位置是KMP算法的亮点,也是难点,只有真正的了解这个回退的特定位置的计算方法,我们才能掌握KMP算法的精髓,再官方的算法里面,每个主串的元素都会对应一个回退的位置,因此我们把每个元素回退的位置放到数组里面,我们称这样的一个数组叫做Next数组

(本人KMP博客是根据下面的这位UP视频所写,除了手动求解我的博客有所涉及解析,其他的代码求解都是简写的(因为我对于其中的诸多细节也不是很通透明了,就不误人子弟了),读者可自行进行系统学习,超详细,链接如下)

【完整版】终于有人讲清楚了KMP算法,Java语言C语言实现_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1UL411E7M8/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=a432cb5e896a2b96961d1f73a6ebe0ca

(3)我们已经知道了Next数组这个概念,我们接下来学习一下这个Next数组里面每个元素的计算方法:

这个就是回退的规则,可能难以理解,我们通过一些具体实例理解一下,

对于初学者我们必须要清楚的是,这个算法是如何用的,通俗的讲,对于一个子串,我们应该看他是否和主串的字符相同,如果相同就进行下一个,如果不相同,我们的子串j就要回退到一个特定的位置,这个位置的求法就是我们要知道的,接下来我们讨论的和练习的都是这个回退下标的计算

可能到这个地方,你大概已经知道了,我们的每一个字符都有一个特定的回退位置,这个组成一个数组,我们称之为Next数组,例如我们的第一个字符回退到2下标的位置,我们就写作Next[0]=2,第二个字符回退到4下标的位置,我们就写作Next[1]=4,以此类推,我们根据规则先手动的求一下这个Next数组,我们现在就要知道怎么手动求解,我们随便给一个字符数组

我们的规定是第一个元素回退到-1下标,第二个回退到0下标(这个记住即可,后面会有用处,可以简单的理解为这个就是规定),从第三个开始,我们就可以根据规则手动求解,第三个c回退到哪个下标,是以a开始,以他前面的b结尾的两个相同的子串,因为只有一个ab,所以我们next[2]=0;第四个字符,我们要找到以a开始,以c结尾的两个字符串,因为这里只有abc,所以next[3]=0;下一个b字符,我们要找到以a开始,以a结尾的两个字符串,他们的长度是1,所以next[4]=1,同理next[5]=2,这样我们就手动求解了这个next数组;

#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
void getnext(char* sub, int* next, int lensub)
{
	next[0] = -1;
	next[1] = 0;
	int i = 2;
	int k = 0;
	while (i < lensub)
	{
		if ((k == -1) || sub[i - 1] == sub[k])
		{
			next[i] = k + 1;
			i++;
			k++;
		}
		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 < 0 || pos >= lenstr)
		return -1;

	int* next = (int*)malloc(sizeof(int) * lenstr);
	assert(next);
	getnext(sub, next, lensub);
	int i = 0;
	int j = 0;
	while (i < lenstr && j < lensub)
	{
		if (j == -1 || str[i] == sub[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];
		}
	}
	if (j >= lensub)
		return i - j;
	else
		return -1;
}
int main()
{
	printf("%d", KMP("abdefabc", "abc", 0));
	return 0;
}

这段代码涉及到了代码表示我们手动计算的值,还有数组的越界访问,找不到和自己一样的字符就会不停的回退,直到相同才会停止,详情请根据视频自行学习;

【完整版】终于有人讲清楚了KMP算法,Java语言C语言实现_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1UL411E7M8/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=a432cb5e896a2b96961d1f73a6ebe0canext数组的优化:就是如果不一样,要不停的回退,我们的优化就是一次性回退到位,回退位置的字符和该字符一样,就写回退位置的nextval值,不一样就写自己的next值(视频也有讲解,读者自行学习)。

相关推荐

  1. C语言KMP算法

    2024-03-30 14:34:01       41 阅读
  2. c语言查找算法

    2024-03-30 14:34:01       44 阅读
  3. kmp算法c++

    2024-03-30 14:34:01       8 阅读
  4. KMP算法

    2024-03-30 14:34:01       52 阅读
  5. KMP算法

    2024-03-30 14:34:01       33 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-30 14:34:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-30 14:34:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-30 14:34:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-30 14:34:01       20 阅读

热门阅读

  1. import 和 export

    2024-03-30 14:34:01       15 阅读
  2. Leetcode704_二分查找

    2024-03-30 14:34:01       23 阅读
  3. XSS 简述及解决

    2024-03-30 14:34:01       20 阅读
  4. Dubbo 几种方式来传递和使用隐式参数

    2024-03-30 14:34:01       18 阅读
  5. Qt_day5:2024/3/26

    2024-03-30 14:34:01       20 阅读
  6. MySQL的日志系统是干什么的?底层原理是什么?

    2024-03-30 14:34:01       16 阅读
  7. [高考] 数学题的一般解题思路

    2024-03-30 14:34:01       18 阅读
  8. 【LeetCode热题100】155. 最小栈(栈)

    2024-03-30 14:34:01       16 阅读