串的模式匹配——暴力匹配和KMP匹配算法

1.暴力匹配(BF算法)

暴力匹配,也称为简单匹配算法,采用穷举的思想,从主串的第一个字符开始和模式串中第一个字符比较。

若相等,则依次比较后续字符,如果不相等,模式串回溯为第一个字符,从主串的第二个字符开始重新和模式串比较,实现如下:

#include<stdio.h>
int BF(char *s,char *t)//s为主串,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;//模式串回溯为0,从头匹配 
		}
	}
	if(t[j]=='\0')//如果j越界,表示模式串遍历完,t是s的字串 
		return i-j;//返回目标串中子串的起始位置 
	else
		return 0;
}
int main()
{
	char s[5]={'a','c','a','b','a'};
	char t[2]={'a','b'};
	printf("%d",BF(s,t));
}

算法时间复杂度分析:

1.最好的情况,即不需要回溯,一次匹配成功,复杂度为O(m)
2.最坏的情况,需要多次回溯时,复杂度为O(n×m)
3.算法平均时间复杂度为O(n×m)

2.KMP算法

该算法相比较暴力算法,有很大的改进,消除了主串指针的回溯,如下图进行字符串s和t的匹配:
在这里插入图片描述用 i 和 j 来扫描字符串s和t,此时匹配失败处为 i = 3,j = 3,虽然本次匹配失败,但仍然取得了部分匹配的信息,s1s2 与 t1t2 相同。

而且这时还发现字符串t中前两个字符和中间两个字符相等,即t1t2 = t0t1,所以s1s2 = t0t1,如下图所示。
在这里插入图片描述

而原本第二趟匹配应该从i = 1,j=0开始匹配,现在既然有s1s2 = t0t1,那么第二趟匹配的 j = 0,j = 1 就可以直接跳过,直接从 i =3, j = 2开始第二趟匹配。

从上匹配过程可以发现,在匹配失败时,我们利用从模式串t中提取的有用信息,消除了主串指针的回溯,这种信息是对于t中的每一个字符 t[j],存在一个整数k使得字符串t开头的k个字符和 t[j] 的前面k个字符相同,如果这样的k存在多个,取最大的那个。

如上述字符串"aaab",当t[3]='b’时,b前面的一个字符"a"和开头的一个字符"a"相同,k=1,b前面的两个字符"aa"和开头的两个字符"aa"相同,即k=2,所以取最大值k=2。

对于模式串t的每个字符,都对应一个k,我们用一个next数组来保存,由模式串t求next数组的算法如下:

void GetNext(char *t,int next[])//求出next数组,消除主串指针回溯 
{
	int j=0,k=-1;//j扫描模式串t,k记录t[j]之前与t开头相同的字符个数 
	next[0]=-1;//开头字符的k值为-1
	while(t[j]!='\0')
	{
		if(k==-1||t[j]==t[k])//扫描到相同字符时,k++
		{
			j++;k++;
			next[j]=k;
		}
		else
		k=next[k];//k回退 
	}
}

取得加速匹配的信息后,当出现匹配失败时,令 j = next[j] ,消除主串指针回溯,KMP算法如下:

#define MaxSize 50
int KMP(char *s,char *t)
{
	int next[MaxSize],length;
	int i=0,j=0;
	GetNext(t,next);
	length=strlen(t);
	while(s[i]!='\0'&&j<length)
	{
		if(j==-1||s[i]==t[j])//j=-1或者当前两个字符相同  
		{
			i++;
			j++;
		}
		else
		{
			j=next[j];//回退到j=next[j],即模式串右滑 
		}
	}
	if(t[j]=='\0')
		return i-j;
	else
		return 0;
}

KMP算法时间复杂度分析:设主串s长度为n,模式串t长度为m,在KMP算法中求next数组的时间复杂度为O(m),在后面的匹配中因主串s的下标不回溯,比较次数可记为n,所以KMP算法平均时间复杂度为O(n+m)。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值