匹配算法

字符串的定位操作通常称为串的模式匹配。模式匹配的应用很常见的函数模式一般是:

int index(const string &Tag, const string &Ptn, int pos)

其中,Tag是主字符串,Ptn是子字符串,如果在主串Tag的第pos个位置后存在与子字符串Ptn相同的子串,返回它在主串第pos个字符后第一次出现的位置,否则返回-1。


一、BF算法:

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







int index(const string &Tag, const string &Ptn, int pos)
{
	int i = pos;//主串当前要比较的位置,初始化为pos
	int j = 0;//子串当前要比较的位置,初始化为0
	int Tlen = Tag.size();//主字符串的长度
	int Plen = Ptn.size();//子字符串的长度

	while (i < Tlen  && j < Plen)
	{
		if (Tag[i] == Ptn[j])//如果当前字符串相同,则继续向下比较
		{
			i++;
			j++;
		}
		else//如果不同,把i和j退回到初始位置,重新进行比较
		{
			i = i - j + 1;
			j = 0;
		}
	}
	if (j >= Plen)//如果比较的长度已经大于子串长度,说明已经匹配成功
	{
		return (i - Plen);
	}
	else
	{
		return -1;
	}
}

二、KMP算法:

BF算法的时间复杂度最坏情况是o(Plen*Tlen),而之所以时间复杂度是由于索引指针的回溯引起的,针对以上不足,于是出现了KMP算法。它的时间复杂度为o(Plen+Tlen)。改进之处在于:每一趟比较重出现的字符不等时,不需要回溯索引指针i,而是利用已经得到的部分匹配结果将子串向右滑动尽可能远的距离,继续进行比较。

如果采用简单的BF算法,则每趟比较i都会要退回,而采用KMP算法,每趟比较时,保持不变,只需要将j向右滑动即可,可即减少了中间一些趟次的比较。

//求next数组中各元素的值,保存在长为len的next数组中
void get_next(const string &Ptn, int *next, int len)
{
	int j = 0;
	int k = -1;
	next[0] = -1;

	while (j<len - 1)
	{
		if (k == -1 || Ptn[j] == Ptn[k])
		{   //如果满足上面分析的Pk = Pj的情况,则继续比较下一个字符,
			//并得next[j+1]=next[j]+1
			j++;
			k++;
			next[j] = k;
		}
		else
		{   //如果符合上面分析的第2种情况,则依据next[k]继续寻找下一个比较的位置
			k = next[k];
		}
	}
}


//求next数组的改进数组中各元素的值,保存在长为len的nextval数组中
void get_nextval(const string &Ptn, int *nextval, int len)
{
	int j = 0;
	int k = -1;
	nextval[0] = -1;

	while (j<len - 1)
	{
		if (k == -1 || Ptn[j] == Ptn[k])
		{   //如果满足上面分析的Pk = Pj的情况,则继续比较下一个字符,
			//并得next[j+1]=next[j]+1
			j++;
			k++;
			if (Ptn[j] != Ptn[k])
				nextval[j] = k;
			else  //Ptn[j]与Ptn[k]相等时,直接跳到netval[k]
				nextval[j] = nextval[k];
		}
		else
		{   //如果符合上面分析的第2种情况,则依据next[k]继续寻找下一个比较的位置
			k = nextval[k];
		}
	}
}

/*
返回子串Ptn在主串Tag的第pos个字符后(含第pos个位置)第一次出现的位置,若不存在,则返回-1
采用KMP算法,这里的位置全部以从0开始计算为准,其中T非空,0<=pos<=Tlen
*/
int kmp_index(const string &Tag, const string &Ptn, int pos)
{
	int i = pos;  //主串当前正待比较的位置,初始为pos
	int j = 0;   //子串当前正待比较的位置,初始为0
	int Tlen = Tag.size();  //主串长度
	int Plen = Ptn.size();  //子串长度

							//求next数组的值,并逐个输出
	int *next = (int *)malloc(Plen * sizeof(int));
	get_next(Ptn, next, Plen);
	//	get_nextval(Ptn,next,Pln);
	int t;
	cout << "子串的next数组中的各元素为:";
	for (t = 0; t<Plen; t++)
		cout << next[t] << " ";
	cout << endl;

	while (i<Tlen && j<Plen)
	{
		if (j == -1 || Tag[i] == Ptn[j])
		{   //如果当前字符相同,或者在子串的第一个字符处失配,则继续向下比较
			i++;
			j++;
		}
		else   //如果当前字符不同,则i保持不变,j变为下一个开始比较的位置
		{
			//next数组时KMP算法的关键,i不回退,
			//而是继续与子串中的next[j]位置的字符进行比较
			j = next[j];
		}
	}

	if (j >= Plen)
		return (i - Plen);
	else
		return -1;
}

(没有写好,有时间再写,未完待续!)



  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值