BF算法及BMP算法的实现

目录

前言

一、BF算法

1.BF算法是什么

2.BF算法的实现

二、KMP算法

1.KMP算法是什么

2.next数组

3.代码实现

总结

前言

例如:随着我们对字符串的不断学习和深入了解,我们会面对一座绕不开的大山——BF算法和BMP算法,本文就介绍了BF BMP算法的基础内容。


一、BF算法

1.BF算法是什么

BF算法是用来查找某一字符串是不是另一字符串的子串与C语言库函数strstr()函数的功能类似

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

我们举一个例子

 BF算法及BMP算法的实现_第1张图片

 上面的字母分别是母串str和子串sub,下面的数字是两个字符串的下标。

i用来控制母串,j用来控制子串,我们需要比较str[i]与sub[j]是否相同来查找sub是不是str的母串,如果是i++,j++;然而当str[i]不等于sub[j]的时候j下标需要回到子串的起始位置,i需要回到母串的上一次查找的后一位置即i=i-j+1;  j=0;这样一直重复循环直到i走到str字符串的最后一个位置,即sub不是str的子串,或者j走到了sub字符串的末尾,即sub是str的子串。

如果sub是str的子串BF会返回子串在母串的起始位置的下标,如果不是子串就会返回-1、

这种算法简单粗暴,但运算速度慢,效率低。最坏情况: 要进行M*(N-M+1)次比较

2.BF算法的实现

下面是我的代码

//BF算法的模拟实现

//des是主串,src是子串
//如果找到返回下标值
//没有找到返回-1
#include
#include
#include


int BF(const char* des, const char* src)
{
	assert(des && src);
	int lenDes = strlen(des);
	int lenSrc = strlen(src);

	int i = 0;
	int j = 0;

	while (i < lenDes && j < lenSrc)
	{
		if (des[i] == src[j])
		{
			i++;
			j++;
		}
		else
		{
			i = i - j + 1;
			j = 0;
		}
	}
	if (j == lenSrc)
	{
		return i - j;
	}
	return -1;

}


int main()
{
	char arr1[] = "awqefuhfnfjudjudjdj";
	char arr2[] = "qef";
	int ret = BF(arr1, arr2);
	if (ret)
	{
		printf("找到了下标是 %d\n", ret);
	}
	else
	{
		printf("没有找到\n");
	}
	return 0;
}

二、KMP算法

1.KMP算法是什么

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

区别KMP和BF唯一不一样的地方在于KMP算法在主串的i不会回退,字串的j也不会回退到0号位置。我们需要在i的前面找到与子串匹配的一部分。

2.next数组

KMP算法的精髓就是next数组,也就是用next[j]=k,来表示,不同的j来对应一个k值,这个k就是你将来移动的j要移动的位置。

而k的值是这样要求的:

1.规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标0开始,另一个以下标j-1结束。

2.不管什么数据next[0]=-1,next[1]=0,在这里,我们以下标开始,而说到的第几个第几个是从1开始。

我们举个例子你就会明白了。

0   1   2   3   4   5

a   b   c   a   b   c     上面是下标。下面是字符串,求它的next数组,

我们j=0时next[0]=-1,j=1时next[1]=0,j=2时,我们要找出以下标是0的元素,到下标是j-1的元素组成的字符串中两个相同的子串的长度就是next数组的值。

经过以上说明,我们可以试着求一下例子中的next数组

举个例子j=4的时候,要查找0到j-1的子串,有abca, a , a三个子串,而abca不是真子串所以只有a,a这两个真子串,所以next[4]=1

0   1   2   3   4   5

-1   0   0   0  1   2

说明:next[0]可以是任意数值,但是将-1赋值给next[0]在代码的实现比较方便

next数组储存的是相等的真子串的长度

这时我们需要通过数学推导总结出公式,才能用代码来描述

p[0]....p[k-1] == p[x]....p[j-1]

p为要求出的next数组的字符串,以上公式通过next数组定义,有两个相同的真子串来表示的,x为符合条件的任意j下标,我们又通过定义可知两个真子串的长度应该是相同的所以

k-1-0==j-1-x

x==j-k;

这时我们引出一个前提next[j]=k;

所以p[0]...p[k-1] p[k]==p[x]...p[i-1] p[i]

所以nex[i]=k;

next[i+1]=k+1;

而当我们的前提不成立的时候,即next[j]!=k;时我们就需要将我们的k回退到下标k所对应的next数组的值即k=next[k];

这样我们就可以避免出现错误,细心的同学会发现当k回退到主串的起始位置时还不成立,k会回退到-1的位置,数组就越界了,我们就在写代码时要注意这个问题。

以上就是KMP算法的理论环节

3.代码实现

#include
#include
#include
#include

void GetNext(char* sub, int* next, int lenSub)
{
	next[0] = -1;
	next[1] = 0;
	int k = 0;//用来记录上一个k
	int i = 2;

	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 (str == NULL || sub == NULL)  
		return -1;
	if (lenStr == 0 || lenSub == 0)  
		return -1;
	if (lenStr < lenSub)  
		return -1;
	if (pos<0 || pos>lenStr)
		return -1;


	int i = pos;
	int j = 0;

	int* next = (int*)malloc(sizeof(int) * lenSub);

	assert(next!=NULL);

	GetNext(sub, next,lenSub);

	while (i < lenStr && j < lenSub)
	{
		if (j==-1||str[i] == sub[j])
		{
			i++; 
			j++;
		}
		else
		{
			j = next[j];
		}
	}
	if (j >= lenSub)
	{
		return i - j;
	}
	return -1;
}


int main()
{
	char arr1[] = "qwedcssfghhuiughj";
	char arr2[] = "huiughj";
	printf("%d\n", KMP(arr1, arr2, 0));//10
	return 0;
}

我们将KMP算法封装在一个函数内部,进入函数时,我们就断言,防止用户传入空指针,并且防止用户传入没有指向字符串的指针,然后按照我们上述的思路实现。

我们会注意到KMP函数内有一个j==-1的情况就是回退到第一个元素后还与字串的元素不同的·情况,i++,j++就可以把数组越界的情况解决,同时我的malloc函数没有free,这是一个无关紧要的小问题。


总结
以上就是今天要讲的内容,本文仅仅简单介绍了BF算法和KMP算法,实际上next数组还可以进一步优化,就不再本文中探讨。

你可能感兴趣的:(算法,c语言)