字符字串模式匹配算法——BF算法与KMP算法

 

 

需解决的问题:从主字符串S中寻找子字符串T出现的第一个位置?如果出现,返回pos,否则,返回-1

一、BF算法

        BF算法就是朴素模式匹配算法,即暴力求解,循环遍历。将子字符串的每个字符和主字符串的字符按顺序比较,如果比较不相等时,两个字符串的索引i和j同时回退,i回退到刚才主字符串起始位置的下一位,子字符串回退到首位,这种方法效率比较低。

如下图:当i和j的位置不相等时,则回退。

//BF算法中索引i和索引j都要回溯,所以效率低
int BF(const char* S, const char* T)
{
	int i = 0, j = 0;
	while (S[i] != '\0' && T[j] != '\0')
	{
		if (S[i] == T[j])
		{
			i++;
			j++;
		}
		else
		{
			i = i - j + 1;//i回溯
			j = 0;//j回溯
		}		
	}
	if (T[j] == '\0') //由于字串结束,则说明找到
	{
		return i - j;
	}
	else//由于主串结束,则说明没有找到
	{
		return -1;
	}
}

二、KMP算法

       KMP算法是为了让BF算法中不必要的回退发生,即i不回退,j回退。问题就在于j回退到哪个位置合适呢?这个回退位置通过一个数组next来记录,next数组的长度等于子串的长度。next数组表示的是当前位置之前的子串的最大公共前后缀长度,当两个字符比较不相等时,则j回退到next[j]的位置。

代码如下:

//j=0,next[j]=-1;表示不进行字符比较
//j>0,next[j]=最长公共前后缀长度

int KMP(const char* S, const char* T)
{
	int i = 0, j = 0;
	int lenS = strlen(S);
	int lenT = strlen(T);
	while (i<lenS && j<lenT)//不用S[i]和S[j]是防止数组越界,因为j=next[j],next[0]=-1
	{
		if (j=-1 || S[i] == T[j])//当j=-1时,需要将j+1让j=0
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];//j回溯到next[j]
		}

	}

	if (j == lenT)
	{
		return i-j;
	}
	else
	{
		return -1;
	}
}

           那么问题就转移到如何求出next数组?如图:j表示前缀,i表示后缀。已知A1前缀和A2后缀相等,比较T[i]和T[j]的情况:

(1)T[i]==T[j],则求值的地方则 next[i+1]=j+1;

 

(2)T[i]!=T[j],因为A1和A2相等,当T[i]和T[j]不等时,则最长子串可能是B1和B3加上后面一位,此时要将j回退到next[j]的位置,此时如果T[j]=T[i],则跳到步骤(1),否则继续回退。代码如下:

void getNext(char* T, int* next)
{
	int j = -1; //j前缀
	int i = 0;//i后缀
	next[0] = -1;
	
	while (i < strlen(T))
	{
		if (j == -1 || T[i] == T[j])
		{
			i++;
			j++;

			next[i] = j;//当T[i] == T[j],则next[i+1]=j+1;就是在刚才的最长公共前后缀长度+1
		}
		else
		{
			j = next[j];/如果T[i] != T[j],则j回退
		}
	}

}

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值