字符串匹配算法之BF算法、KMP算法

目录

1.BF算法(Brute Force)

(1)BF算法简介

(2)BF算法思想(实现思想)

(3)BF算法实现代码(C语言)

(4)BF算法的改进

 2.KMP算法

(1)next数组的求法(默认模式串下标从0开始)

(2)求next数组算法思想(代码实现)

(3)KMP算法代码实现

(4)KMP算法的优化(引入nextval数组)


1.BF算法(Brute Force)

(1)BF算法简介

BF算法即暴力算法。它的思想就是将模式串和主串的字符进行一一比较:模式串和子串进行比较,一致时继续比较主串和模式串该趟的下一个字符,直到比较完整的整个模式串;不一致时模式串后移一位,以主串的下一个字符为起始,模式串从首位重新开始比较。重复上面的步骤。(如下图)

字符串匹配算法之BF算法、KMP算法_第1张图片

(2)BF算法思想(实现思想)

申请两个变量i、j分别指向主串和模式串开始。只要i、j均在主串和模式串长度内:如果i、j指向的字符相同则两个变量均++,比对下一个字符;否则i回退到主串下一个字符(i-j+1)模式串回退到0重新开始比较。 若任一方超出长度范围:判断若为j超范围则已找到,返回i-j。

(3)BF算法实现代码(C语言)

#include
#include
#include
#include

int BF_Search(const char* str, const char* sub)
{
	assert(str != NULL && sub != NULL);//断言
	int len_str = strlen(str);
	int len_sub = strlen(sub);
	int i = 0;
	int j = 0;

	while (i < len_str && j < len_sub)//i、j均在长度范围内~循环判断
	{
		if (str[i] == sub[j])//字符比对相等~看下一个字符
		{
			i++;
			j++;
		}
		else
		{
			i = i - j + 1;//i回退到以下一个字符为起始重新开始比较
			j = 0;//j每次回退到0
		}
	}
	if (j >= len_sub) return i - j;//退出循环是j超出范围~已找到~返回i-j
	return -1;
}

int main()
{
	const char* str = "abcabcdabcde";
	const char* sub = "abcd";
	int index = BF_Search(str, sub);
	printf("模式串在主串中第一次出现的起始位置下标为:%d\n", index);
	return 0;
}

(4)BF算法的改进

其实改进只是规定了模式串匹配是从主串的下标为pos的位置开始的。

可以给BF_Search()函数加参数int pos,再将i的初始值改为pos即可。(如图)

字符串匹配算法之BF算法、KMP算法_第2张图片

 2.KMP算法

KMP算法的最主要思想:i坚决不回退,j回退到合适位置(next数组/nextval数组)。

(1)next数组的求法(默认模式串下标从0开始)

其实next数组就是看最长公共前后缀(next数组前两个初值默认-1,0)。

字符串匹配算法之BF算法、KMP算法_第3张图片

(2)求next数组算法思想(代码实现)

算法思想:【根据已知求未知】【next数组只与模式串有关】

定义变量i保存当前已知next数组值的最后一个字符下标(初值1),变量j保存i对应的回退位置下标(next[i],初值0)。求下标[i+1~len-1]的next数组值:只要i、j对应模式串字符相同或j==-1(j回退到不能再回退),则j++,next[++i]=j; 开始判断下一个;否则j按照next数组值回退继续循环判断(j=next[j];)。

代码实现:

int* Get_Next(const char* sub)
{
	assert(sub != NULL);
	int len = strlen(sub);
	int* next = (int*)malloc(sizeof(int) * len);
	assert(next != NULL);
	next[0] = -1;//默认模式串下标从0开始
	next[1] = 0;

	int i = 1;//变量i保存当前已知next数组值的最后一个字符下标(初值1)变量j保存i对应的回退位置下标(next[i],初值0)
	int j = 0;//变量j保存i对应的回退位置下标(next[i],初值0)
	while (i + 1 < len)
	{
		if (j == -1 || sub[i] == sub[j])//i、j对应模式串字符相同或j==-1
		{
			j++;
			next[++i] = j;
		}
		else j = next[j];//j按照next数组值回退继续循环判断(j=next[j];)
	}
	return next;
}

(3)KMP算法代码实现

KMP只是对BF算法的改进:i坚决不回退,j按照next数组对应值回退。

代码如下:

#include
#include
#include
#include
#include

int* Get_Next(const char* sub)
{
	assert(sub != NULL);
	int len = strlen(sub);
	int* next = (int*)malloc(sizeof(int) * len);
	assert(next != NULL);
	next[0] = -1;//默认模式串下标从0开始
	next[1] = 0;

	int i = 1;//变量i保存当前已知next数组值的最后一个字符下标(初值1)
	int j = 0;//变量j保存i对应的回退位置下标(next[i],初值0)
	while (i + 1 < len)
	{
		if (j == -1 || sub[i] == sub[j])//i、j对应模式串字符相同或j==-1
		{
			j++;
			next[++i] = j;
		}
		else j = next[j];//j按照next数组值回退继续循环判断(j=next[j];)
	}
	return next;
}

int KMP_Search(const char* str, const char* sub,int pos)
{
	assert(str != NULL && sub != NULL);//断言
	int len_str = strlen(str);
	int len_sub = strlen(sub);
	assert(pos >= 0 && pos < len_str);//pos合法
	int i = pos;//i指向主串开始位置
	int j = 0;//j指模式串开始位置
	
	int* next = Get_Next(sub);
	while (i < len_str && j < len_sub)//i、j均在长度范围内~循环判断
	{
		if (j==-1||str[i] == sub[j])//字符比对相等或j==-1无法回退~i++、j++
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];//i不回退,j按照next数组回退
		}
	}
	if (j >= len_sub) return i - j;//退出循环是j超出范围~已找到~返回i-j
	return -1;
}

int main()
{
	const char* str = "abcabcdabcde";
	const char* sub = "abcd";
	int index = KMP_Search(str, sub,0);
	printf("模式串在主串(以0为主串起始匹配下标)中出现的起始位置下标为:%d\n", index);
	index = KMP_Search(str, sub, 5);
	printf("模式串在主串(以5为主串起始匹配下标)中出现的起始位置下标为:%d\n", index);
	return 0;
}

(4)KMP算法的优化(引入nextval数组)

nextval[]:其实是对next数组的改进(在求出next数组的基础上)。当模式串i指向的位置字符==其对应next数组回退位置的对应字符时:nextval [ i ] =nextval [ next [ i ] ] ; 否则直接赋值:nextval[i]=next[i];

代码如下:

int* Get_Nextval(const char* sub)
{
	assert(sub != NULL);
	int len = strlen(sub);
	int* next = (int*)malloc(sizeof(int) * len);
	int* nextval = (int*)malloc(sizeof(int) * len);
	assert(next != NULL && nextval != NULL);

	next[0] = -1;//默认模式串下标从0开始
	next[1] = 0;

	int i = 1;//变量i保存当前已知next数组值的最后一个字符下标(初值1)
	int j = 0;//变量j保存i对应的回退位置下标(next[i],初值0)
	while (i + 1 < len)
	{
		if (j == -1 || sub[i] == sub[j])
		{
			j++;
			next[++i] = j;
		}
		else j = next[j];
	}
	//next数组已求出

	nextval[0] = -1;//固定
	for (int m = 1; m < len; ++m)
	{
		if (sub[m] == sub[next[m]])//m对应字符==m的回退位置对应字符 ~ nextval的值=回退位置已经赋的nextval数组的值
		{
			nextval[m] = nextval[next[m]];
		}
		nextval[m] = next[m];//否则直接用next数组的值赋值
	}
	free(next);
	return nextval;
}

你可能感兴趣的:(C语言,数据结构与算法,算法,蓝桥杯,c语言,数据结构)