数据结构考研第4章——串(内含动图)

6 篇文章 3 订阅
3 篇文章 0 订阅

提示:关于串与模式匹配的基本概念较为简单,可以自行学习,下面与大家直接讨论模式匹配算法。

一、简单的模式匹配算法

【代码实现】

int Index(SString S,SString T){
	int i=1,j=1;
	while(i<=S.length&&j<=T.length){
		if(S.ch[i]==T.ch[j]){
			++i;		//后面记得i要减回1,在i-T.length实现
			++j;		//最后一下匹配成功了也要加1,用于后面判断(j>T.length)
		}
		else{
			i=i-j+2;	//i=i-(j-1)+1,因为i前进了(j-1)步,现在后退(j-1)步,然后向前1步
			j=1;
		}
	}
	if(j>T.length) return i-T.length;		//(i-1)-(T.length-1)=i-T.length
	else return 0;
}

【具体实例】在这里插入图片描述

二、串的模式匹配算法——KMP算法

【算法思想】 (对于下面这个过程,从“前缀跟后缀相同”出发,思考为什么这样移动)

  1. 问题

主串(a b a b c a b c a c b a b)模式(a b c a c)

  1. 解答
  1. 对模式制作部分值PM
    ‘a’的前缀为空,后缀为空,两者交集为空;
    ‘ab’的前缀为{a},后缀为{b},两者交集为空;
    ‘abc’的前缀为{a,ab},后缀为{bc,c},两者交集为空;
    'abca’的前缀为{a,ab,abc},后缀{bca,ca,a},两者交集为{a};
    ‘abcac’的前缀为{a,ab,abc,abca},后缀{bcac,cac,ac,c},两者交集为空
    在这里插入图片描述
  1. 利用上述得到的部分匹配值PM完成匹配
    【第一趟匹配过程】
    发现a与c不匹配,前两个字符是匹配的,查表可知,最后一个匹配字符b对应的部分匹配值为0,因此:移动位数=已匹配的字符数 - 对应的部分匹配值=2-0=2,所以将子串向后移动2位。j=1+PM
    【第二趟匹配过程】
    发现b与c不匹配,前四个字符是匹配的,查表可知,最后一个匹配字符a对应的部分匹配值为1,因此:移动位数=已匹配的字符数 - 对应的部分匹配值=4-1=3,所以将子串向后移动3位。j=1+PM
    【第三趟匹配过程】
    成功
    在这里插入图片描述

【具体实例】
在这里插入图片描述

三、串的模式匹配——KMP算法改进(对next数组改进)

(提示:如果上面的KMP算法还没弄明白,那么下面的基本就不用看了,应该回溯上述思想)

  1. 【对算法的第一次改进】
  1. 已知:右移位数=已匹配的字符数 - 对应的部分匹配值,即为Move=(j-1)- PM[j-1];
    使用部分匹配值时,每当匹配失败,就去找它前一个元素的部分匹配值,这样使用起来有些不方便,所以将PM表右移一位,这样哪个元素匹配失败,直接看它自己的部分匹配值即可。
  2. 可得:将上例中字符串’abcac’的PM表右移一位,就得到了next数组。其中第一位元素右移后空缺的用-1来填充,最后一位溢出了也没关系。
    在这里插入图片描述
  3. 最后可以得到公式:
    Move =(j-1)- next[j]
    j = j-Move = next[j]+1
  1. 【对算法的第二次改进】
  1. 有时候为了让公式变得更加简洁,可以将next数组整体+1;
    在这里插入图片描述
  2. 最后可以得到公式:
    Move =(j-1)-(next[j]-1)=j-next[j]
    j = next[j]
  1. 【next数组求取的具体代码】
    (鄙人才疏学浅,没有很好的语言组织能力,需要读者自行将具体实例的i和j走一遍,自行领悟,谢谢)
void get_next(String T,int next[]){
	int i=1,j=0;
	next[1]=0;
	while(i<T.length){
//这一段是重点,规律就在这里,总结为:循环找到,就在找到的编号+1;循环找不到,就直接等于1;
		if(j==0||T.ch[i]==T.ch[j]){
			++i,++j;
			next[i]=j;
		}
		else j=next[j];
	}
}
  1. 【具体实例】
    在这里插入图片描述
  2. 【KMP匹配算法】
int Index(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{	        	//不同的话就回溯,往往这里进行一次,上面S.ch[i]==T.ch[j]就成立了
			j=next[j];
		}
	}
	if(j>T.length) return i-T.length;		//(i-1)-(T.length-1)=i-T.length
	else return 0;
}
  1. 【算法分析】
  1. 尽管普通模式匹配时间复杂度是O(mn),KMP算法的时间复杂度是O(m+n),但是一般情况下,普通模式匹配的实际执行时间近似为O(m+n),因此至今仍被使用。
  2. KMP算法仅在主串与子串有很多“部分匹配”时才显得比普通算法快得多,其主要优点是主串不回溯。

四、串的模式匹配——KMP算法改进(对nextval数组改进)

(对nextval数组,可以在《王道考研》P118了解)

  1. nextval数组求取
void get_next(String T,int next[]){
	int i=1,j=0;
	nextval[1]=0;
	while(i<T.length){
//这一段是重点,规律就在这里,总结为:循环找到,就在找到的编号+1;循环找不到,就直接等于1;
		if(j==0||T.ch[i]==T.ch[j]){
			++i,++j;
			if(T.ch[i]!=T.ch[j]) nextval[i]=j;		//与next数组的求取,区别就是对nextval[i]=j加上了条件判断
			else nextval[i]=nextval[j];
		}
		else j=nextval[j];
	}
}
  1. 具体实例——使用nextval数组的KMP算法
    (上面使用next数组的KMP算法中,把next数组名字改成nextval数组就可以了)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值