数据结构与算法(9)

串的模式匹配算法

子串的定位运算通常称为串的模式匹配或串匹配。

设有两个字符串S和T,设S为主串,也称正文串;设T为子串,也称为模式。在主串S中查找与模式T相匹配的子串,如果匹配成功,确定相匹配的子串中的第一个字符在主串中出现的位置。

1、BF算法

最简单直观的模式匹配算法是BF(Brute-Force)算法。

模式匹配不一定是从主串的第一个位置开始,可以指定主串中查找的起始位置pos。如果采用字符串顺序储存结构,可以写出不依赖与其他串操作的匹配算法。

算法步骤:

1.分别利用计数指针i和j指示主串S和模式T中当前正待比较的字符位置,i初值为pos,j初值为1。

2.如果两个串均未比较到串尾,即i和j均小于S和T的长度时,则循环执行以下的操作:

        1)S.ch[i]和T.ch[j]比较,若相等,则i和j分别指示串中下个位置,继续比较后续字符;

2)若不相等,指针后退重新开始匹配,从主串的下一个字符(i=i-j+2)起再重新和模式的第一个字符(j=1)比较。

3.如果T.length,说明模式T中的每个字符依次和主串S中的一个连续的字符序列相同,则匹配成功,返回和模式T中第一个字符相等的字符再主串S中的序号(i-T.length);否则称匹配不成功,返回0.

int Index_BF(SString s,SString T,int pos)
{
	//返回模式T在主串中第pos个字符开始第一次出现的位置。若不存在,则返回0 
	//其中T非空,1<=pos<=S.length 
	i=pos;//初始化 
	j=1;
	while(i<=S.length&&j<=T.length)//两串均未比较到串尾 
	{
		if(S.ch[i]==T.ch[j])//继续比较后续字符 
		{
			i++;
			j++
		}
		else//指针后退,重新开始匹配 
		{
			i=i-j+2;
			j=1;
		}
		if(j>T.length)//匹配成功 
			return i-T.length;
		else//匹配失败 
			return 0;
	}
 } 

2、KMP算法

KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作。其改进在于:每一趟匹配过程中出现字符不相等时,无需回溯主串的指针 ,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后继续进行比较。

假设主串为“s1 s2 ··· sn”,模式串为“t1 t2 ··· tm”,为了改进算法,需要解决下面的一个问题:当匹配过程中产生失配(si!=tj)时,模式串可以向右滑动有多远。

假设此时应该与模式中第k(k<j)个字符继续比较,则模式中前k-1给字符的子串必须满足下列关系式:

"t1 t2 ··· tk-1"="si-k+1 si-k=2 ··· si-1"

已经得到的“部分匹配”的结果是:
"tj-k +1 tj-k+2 ··· tj-1"="si-k+1 si-k+2 ··· si-1"

由以上的两个式子可以推出:

"t1 t2 ··· tk-1"="tj-k+1 tj-k+2 ··· tj-1"

反之,若模式串中存在满足第三个式子的两个子串,则当匹配过程中,主串中的第i个字符与模式中第j个字符不等时,仅需将模式向右滑动至模式中第k个字符与主串第i个字符对齐,此时,模式中头k-1个字符的子串"t1 t2 ··· tk-1"必定与主串中第i个字符之前长度为k-1的子串"si-k+1 si-k+2 ··· si-1"相等。由此,匹配仅需从模式中第k个字符与主串中第i个字符开始,依次向后进行比较。

若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应的字符失配时,在模式中需重新和主串中该字符进行比较的字符位置。由此可以引出模式串next函数的定义为:

next[j]=0  j=1(t1与si不相等时,下一步进行t1与 si-1的比较)

next[j]=Max{k| 1<k<j,且有”t1 t2 ··· tk-1“=”tj-k+1 tj-k+2 ··· tj-1“}

next[j]=1 k=1(不存在相同的子串,下一步进行t1与si的比较)

由此定义可以推出模式串next的函数值。

KPM算法,当在匹配过程中产生失配时,指针i不变,指针j退回到next[j]所指示的位置上重新比较,并且当指针j退回0时,指针i和指针j需同时增1。即若主串的第i个字符和模式的1个字符不等时,应从主串的第i个字符不等,应从主串的第i+1个字符重新进行匹配。

int Index_KPM(SString S,SString T,int pos)
{
	//利用模式串T的next函数T在主串S中第pos个字符后的位置 
	//其中,T非空,1<=pos<=S.length 
	i=pos;
	j=1;
	while(i<=S.length&&j<=T.length)//两串均未比较到串尾 
	{
		if(j==0||S.ch[i]==T.ch[j])//继续比较后续字符 
		{
			i++;
			j++;
		}
		else//模式串向右移动 
			j=next[j];
	}
	if(j>T.length)
		return i-T.length;//匹配成功 
	else
		return 0;//匹配失败 
}

计算next函数值的方法:

void ger_next(SString T,int next[])
{
	//求模式串T的next函数值并将其存入数组next 
	i=1;
	next[0]=0;
	j=0;
	while(i<T.length)
	{
		if(j==0||T.ch[j])
		{
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j]
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值