BF算法、KMP算法、改进的KMP算法

BF算法、KMP算法、改进的KMP算法,以上几种算法都是模式匹配算法,即找模式串在主串中第一次出现的位置。

BF算法:

如上图所示 主串:”abcaba”   子串:”aba”

思路:从主串第一个开始和子串第一个匹配,如果相等则两个串都向后走一步,继续匹配。如果不相等则主串从i-j+1开始、子串从头开始重新匹配。可以看到每当失配时,主串的i需要从开始匹配的下一个字符开始,重新和子串开始匹配。即每次失配了就执行 i=i-j+1,j=0操作。整个时间复杂度O(n*m)。n表示主串长度、m表示子串长度。

代码:

int BF(const char* s,const char* sub,int pos)
{
	int i=pos;
	int j=0;
	int lens=strlen(s);
	int lensub=strlen(sub);
	while(i<lens && j<lensub)
	{
		if(s[i]==sub[j])
		{
			i++;
			j++;
		}
		else
		{
			i=i-j+1;
			j=0;
		}
	}
	if(j>=lensub)
	{
		return i-j;
	}
	return -1;
}

KMP算法的实现:

思路:主串的i不回退,子串的j也只回退到指定的位置,而不用每次都会退到0。因为有些回退是没有必要的。而具体j回退到哪个位置,就需要我们写next数组了。

next数组存在的意义:不用将j回退到0,而是回退到next数组的值

代码:

扫描二维码关注公众号,回复: 2772203 查看本文章
void GetNext(int* next,const char* sub)
{
	int lensub=strlen(sub);
	next[0]=-1;
	next[1]=0;

	int i=2;//开始从第3位,下标为2的开始,是匹配该位之前的
	int k=0;
	while(i<lensub)
	{
		if((k==-1) || sub[k]==sub[i-1])//如果两个相等
		{
			next[i++]=++k;//i++表示当前i的next得出来后,之后计算i的下一个next。++k是相等情况下k+1;
		}
		else
		{
			k=next[k];//k处失配的
		}
	}
}
int KMP(const char* s,const char* sub,int pos)
{
	int i=pos;
	int j=0;
	int lens=strlen(s);
	int lensub=strlen(sub);
	int* next=(int*)malloc(sizeof(int)*lensub);
	GetNext(next,sub);
	while(i<lens && j<lensub)
	{
		if(j==-1 || s[i]==sub[j])
		{
			i++;
			j++;
		}
		else
		{
			j=next[j];//next[j]保存的是j位置失配时应该回退的位置
		}
	}
	free(next);
	if(j>=lensub)
	{
		return i-j;
	}
	return -1;
}

改进的KMP算法:

nextval代码:

void GetNextVal(int* nextval,const char* sub)
{
	int lensub=strlen(sub);
	nextval[0]=-1;
	int i=0;
	int k=-1;
	while(i<lensub)
	{
		if((k==-1) || sub[k]==sub[i])
		{
			++i;
			++k;
			if(sub[k]==sub[i])
			{
				nextval[i]=nextval[k];
			}
			else if(i<lensub)//可能当前i<lensub,因为 k==-1而执行了++i操作,导致后面nextval[i]越界
			{
				nextval[i]=k;
			}
		}
		else
		{
			k=nextval[k];//k处失配的
		}
	}
}

 

下面我们来整体测试一些代码

int main()
{
	const char* s="abcaabbcabcaabdab";
	const char* sub="abbc";
	cout<<BF(s,sub,0)<<endl;
	cout<<KMP(s,sub,0)<<endl;
	return 0;
}

运行结果:

为查看next数组和nextval数组添加一个打印函数

void Print(int *arr,int len)
{
	for(int i=0;i<len;i++)
	{
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}

现在来看一下两个数组的结果:

int main()
{
	const char* s="abcaabbcabcaabdab";
	const char* sub="aaaaaaaab";
	int lensub=strlen(sub);
	int lens=strlen(s);
	int* next=(int*)malloc(sizeof(int)*lens);
	int* nextval=(int*)malloc(sizeof(int)*lens);
	GetNext(next,sub);
	GetNextVal(nextval,sub);
	Print(next,lensub);
	Print(nextval,lensub);
	GetNext(next,s);
	GetNextVal(nextval,s);
	Print(next,lens);
	Print(nextval,lens);
	return 0;
}

结果:

和我们想的一样。但是有些书上起始是从0开始而不是从-1开始,这样的话我们还是按这种方法计算next数组,然后将数组的每个值都加1就好了。

猜你喜欢

转载自blog.csdn.net/ShWe_yayaya/article/details/81671220