DS 0925(第四章 关于串的算法)

本文介绍了串的模式匹配问题,包括朴素模式匹配算法及其时间复杂度分析,接着详细阐述了KMP算法的工作原理,强调了KMP算法不回溯的特点,并通过例子解释了next数组的构建和作用。最后提到了KMP算法的平均时间复杂度以及nextval数组的优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

串的朴素模式匹配算法

模式匹配

主串:S=‘abcdefg’

子串:‘abc’ , ‘efg’ //子串一定是主串中存在的

模式串:‘cde’ ,‘ifg’ //模式串是想要在主串中找到的,未必存在

串的模式匹配: 在主串中找到与模式串相同的子串,并返回其所在的位置

int Index(SString S,SString T){
	int k=1;				//使用变量k来表示当前取出子串的起始位置 
	int i=k,j=1;			//使用i和j来分别指向两个串的对应位置 
	while(i<=S.length&&j<=T.length){
		if(S.ch[i]==T.ch[j]){
			++i;
			++j;     		//继续比较后继字符 
		} else{
			k++;  			//检查下一个子串
			i=k;
			j=1; 
		}
	}
	if(j>T.length)
	return k;
	else
	return 0;
} 

算法性能分析:(若模式串长度为m,主串长度为n)

1.较好的情况:每个子串第一个字符就不匹配

2.匹配成功的最好情况:O(m)

3.匹配失败的最好情况:O(n-m+1)=O(n-m)=O(n)

4.最坏情况:n-m+1 匹配m次 ,O(nm)

朴素模式匹配算法(简单模式匹配算法):
1.将主串中和模式串长度相同的串先搞出来,挨个与模式串进行对比
2.当模式串与子串某个字符不匹配时,就立即放弃当前子串,转而检索下一个子串

KMP算法

朴素模式匹配算法的缺点: 用于在主串中记录当前匹配字符的指针i需要不断回溯,浪费时间。

KMP算法特点

1.用于记录主串位置的i指针不回溯

2.通过对模式串的判断,当j=k时匹配失败,说明1~k-1都匹配成功
eg:模式串为 ‘google’

  • 若当前两个字符匹配i++,j++
  • 若j=1时发生不匹配,则应让i++,而j依然是1
  • 若j=2时发生不匹配,则应让j回到1
  • 若j=3时发生不匹配,则应让j回到1
  • 若j=4时发生不匹配,则应让j回到1
  • 若j=5时发生不匹配,则应让j回到2
  • 若j=6时发生不匹配,则应让j回到1

由此可以建立一个数组next ,当j=k且字符不匹配时,令j=next[k];

int Index_KMP(SString S,SString T,int next[]){
	int i=1, 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数组的快速求法

eg:‘abcadb’

  • 若j=6时发生不匹配,则令j=next[6]=3;

eg:‘abababcdef’

  • 若j=7时发生不匹配,则令j=next[7]=5;
  • 对于上面的情况,不可以令j=next[7]=3,会错过很多字符

eg:‘aaaabcd’

  • 若j=5时发生不匹配,则令j=next[5]=4;

eg:‘abcdefg’

  • 若j=5时发生不匹配,则令j=next[5]=1;

eg:‘abcabd’

  • 若j=1时发生不匹配,则令j=next[1]=0;之后再让i++,j++

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

  1. 当第 j 个字符匹配失败时,由1~j-1个字符组成的串记为S,则:next[j]=S的最长相等前后缀长度+1
  2. 特别的next[1]=0

练习1:
模式串:‘ababaa’

序号j123456
模式串ababaa
next[j}011234

练习2:
模式串:‘aaaab’

序号j12345
模式串aaaab
next[j]01234

KMP算法的平均时间复杂度:O(m+n)

//求模式串T的next数组 O(m)
void get_next(SString T,int next[]){
	int i=1,j=0;
	next[1]=0;
	while(i<T.length){
		if(j==0||T.ch[i]==T.ch[j]){
			++i;
			++j;
			next[i]=j;
		}
		else  
		j=next[j];     //循环继续 
	}
} 



int Index_KMP(SString S,SString T){
	int i=1, j=1;
	int next[T.length+1];
	get_next(T,next);    //求模式串的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算法的优化:nextval[j]数组

作用:避免了j指针回溯到前一个相同的字母,进行一些重复的不必要的比较

nextval[j]数组的求法

  1. 令nextval[0]=0
  2. 往后看下一个字符的next数组所指向的字符,与其是否相同,相同的话则令其nextval数组值等于相同字符的nextval数组值
  3. 如果不相同nextval数组值,则等于next数组值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值