串的模式匹配——KMP && BF

BF算法匹配的话,确实是最容易想到和实现的,也难怪现在还是广泛应用中,一般情况下,BF算法的效率还是可以的,但是最坏情况下就不行了,KMP算法就解决了这个问题,相应地,当然比BF算法复杂了,一开始看KMP 真的不知道它在说啥,看了几遍才理清思路。

 

KMP的主要思想(有一些自己的理解):匹配模式T和主串T,在已经匹配了j-1个的情况下,如果现在S[i]和T[j-1]不相等,那么如果是BF算法,就从T[0]重新开始,而且i坐标也要回去,这样就浪费了时间,KMP算法则是从已经匹配的j-1中选择一个位置k,从位置k开始,进行S[i]和T[k]的比较,找出这个k就是KMP的高明和困难之处。

 

假设现在S[i-1]应该与T中的第k(k<j)个字符继续比较,则模式T中第k个字符之前的k-1个字符必定与主串S中第i个字符之前的k-1个字符相等。

即有 (T[0],T[1],.......,T[k-2])= (S[i-k],S[i-k+1],......,S[i-2])

而已经得到模式T和主串S“部分匹配”的结果是

(T[0],T[1],.....,T[J-K],T[J-K+1],.....T[J-2])=(S[i-j],S[i-j+1],.....,S[i-k],S[i-k+1],.......,S[i-2])

综合上面两个式子,可得(T[0],T[1],......,T[K-2])=(T[J-K],T[J-K+1],.......,T[J-2]);

所以只要找出满足上式的k就可以让,S的第i个字符与模式T的第k个字符开始比较,也就省下了时间。

求K的过程,KMP是用一个next[]的整形数组,这个数组的定义是这样的:

1、当j=0时,next[j]=-1;

2、max{k|0<kj-1 且  T[0],T[1],......,T[K-2])=(T[J-K],T[J-K+1],.......,T[J-2]);}

3、0 其他情况

 

求解next[j]的方法:

 设模式T为"abaabcac"

序号01234567
T[j]abaabcac
k0011201
T[k]与T[j]比较!===!===!===!=
next[j]-10011201

 假设现在next[0]~next[4]都已经球出来了,现在要求next[5],求解过程是这样的:

令k=next[4]=1,比较T[4]与T[1],T[4]==T[1],所以next[5]=next[4]+1=k+1=2;

如果要求next[6],那么就令k=next[5]=2,T[5]!=T[2],令k=next[2]=0,T[5]!=T[0],令k=next[0]=-1,此时,当k=-1时,就默认是寻找k的过程结束了,此时就和上面一样,next[6]=next[5]+1=k+1=0;

由上面两个例子可以知道,求解next[j]就是先令k等于next[j-1],然后就是比较T[j-1]与T[k],不想等时,就令k=next[k],如此反复做下去,直到T[j-1]==T[k]或者是k=-1为止,

此时next[j]=k+1;

 

下面是KMP 和 BF 的代码

文件"mystring.h"

#include<string>
using namespace std;

//BF算法
int Index_BF(string S,string T,int pos)
{
	//中要满足1<=pos<=S.getlength()-T.getlength()+1
	int i=pos-1,j=0;
	while(S[i+j]!='\0' && T[j]!='\0')
	{
		if(S[i+j]==T[j])
			j++;
		else
		{
			i++;
			j=0;
		}
	}
	if(T[j]=='\0')
		return i;
	else
		return -1;
}

//下面是KMP算法
void Get_next(string T,int next[])
{
	//求模式T的next函数值并存入数组next中,为后面的模式匹配做准备
	int k=-1,j=0;
	next[0]=-1;
	int length=T.length();
	while(j<length)
	{
		if( (k==-1)||(T[j]==T[k]) )
		{
			++j;
			++k;
			next[j]=k;
		}
		else
			k=next[k];
	}
}

int Index_KMP(string S,string T,int pos,int next[])
{
	//利用模式T的next函数求模式T在主串S的第pos个字符后面的位置
	//其中要满足1<=pos<=S.getlength()-T.getlength()+1
	
	int i=pos-1,j=0;
	int n=S.length();
	int m=T.length();
	while(i<n && j<m)
	{
		if(j==-1 || S[i]==T[j])//j=-1时,是在T的第一个元素就和S[i]不相等时
		{
			++i;
			++j;
		}
		else
			j=next[j];
	}
	if(j>=m)
		return i-m;//匹配成功
	else
		return -1;//匹配失败
}


主函数测试"main.cpp"文件

#include"mystring.h"
#include<iostream>
using namespace std;

int main()
{
	string S("wangjikaiailiudan");
	string T("liudan");
	
	int next[1024];
	memset(next,0,sizeof(next));
	Get_next(T,next);
	
	int pos1=Index_KMP(S,T,7,next);
	int pos2=Index_BF(S,T,8);

	if(pos1==-1 && pos2==-1)
		cout<<"无法匹配"<<endl;
	else
		cout<<"模式T在主窜S的第 "<<(pos1+pos2)/2<<" 号位置开始匹配成功"<<endl;
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值