字符串匹配


在主串中找到和模式串相同的子串,并返回其所在位置。下面考虑的主串和模式串的的初始位置都是从数组下标为1的位置。

朴素模式匹配

将主串中和模式串长度相同的子串和模式串一次比较,只要有一个字符不匹配,就舍弃当前子串比较下一个子串

主串长度为n,模式串长度为m,则最坏时间复杂度为 O ( n m ) O(nm) O(nm)(要匹配(n-m+1)m次)

朴素模式四配算法的缺点: 当某些子串与模式串能部分四配时,主串的扫描指针 i 经 常回溯,导致时间开销增加。最坏时间复杂度O(nm)

代码实现:

  • 法1
int Index(SString S, SString T){
	int k=1;
	int i=k;
	int j=1;
	while(i<S.length && j<=T.length){
		if(S.ch[i]==T.ch[j]){
			i++;
			j++;		//继续比较后续字符
		} else{
			k++;		//检查下一个子串
			i=k;
			j=1;
		}
	//匹配成功时j=T.length+1
	if(j>T.length)
		return k;
	else
		return 0;
  • 法2
int Index(SString S, SString T){
	int i=1;
	int 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;
		}
	//匹配成功时j=T.length+1
	if(j>T.length)
		return i-T.length;
	else
		return 0;

KMP算法

基本思想

KMP算法是对朴素匹配算法的改进。

  • 每次回溯模式串的指针,主串的指针不再回溯。
  • 通过增加一个回溯数组(next数组),代表模式串当前位置不匹配的时候回溯的位置
  • 注意一个特殊的情况就是,当第一个字符不匹配的时候,主串的指针和模式串的指针都应后移再开始下一次匹配,因此此时对应的模式串的指针回溯到0,然后让主串和模式串的指针都移动到下一个位置再开始匹配

特点:当子串和模式串不匹配时,主串指针 i 不回溯,模式串指针 j=next[j] 算法平均时间复杂度: O(n+m)

next数组确定

串的前缀: 包含第一个字符,且不包含最后一个字符的子串
串的后缀: 包含最后一个字符,且不包含第一个字符的子串

对于模式串,当第j个字符匹配失败,由前j-1 个字符组成的串记为S,则next[j]= (S的最长相等前后缀长度+1)。 特别地,next[1]=0

代码实现

  • 求模式串的next数组
void get_next(SString T, int next[]){
	int i=1;
	int j=0;
	while(i<T.length){
		if(j==0 || T.ch[i]==T.ch[j]){
			i++;
			j++;
			//若pi=pj,则next[j+1]=next[j]+1
			next[i]=j;
		}
		else
			j=next[j];
	}
}
  • KMP算法
int Index_KMP(SString S, SString T){
	int i=1;
	int j=1;
	int next[T.length+1];
	get_next(T, next);
	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;
}

KMP算法改进

KMP算法的缺点:存在无意义的对比。比如aaaab和aaacaaaab…匹配,匹配到第4个位置的时候不匹配,使用next数组(next数组的值为[0, 1, 2, 3, 4])则前3个a依次回退,但是通过模式串我们已知模式串前四个字符相等,则前三个字符和c必然也不匹配,就没必要进行比较,应该直接回溯到0。

改进方法:使用nextval数组(nextval是对next数组的优化)而不使用next数组。

//先计算出next数组
//令nextval[1]=0
for(int j=2;j<=T.length;j++){
	if(T.ch[next[j]]==T.ch[j])
		nextval[j]=nextval[next[j]];
	else
		nextval[j]=next[j];
}

内容来源:王道考研-数据结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值