数据结构串分析笔记

串是一种数据结构,是一种特殊的线性表。

特点

1、其内数据元素都来自字符集。
2、由于其数据元素特殊,所以有些操作不同于一般线性表,例如其操作对象一般是子串(即一组数据元素),而不是单个的数据元素。

串的相关名词

:由零个或多个字符组成的有限序列。
空串:含零个字符的串。
空格串:由空格组成的串。
串长:串中所含字符的个数。
串相等:两个字符串的长度相等并且各个对应位置上的字符都相等。
子串:一个串中任意个连续字符组成的子序列(含空串、空格串、串本身)。
真子串:除本身以外的所有子串(即串本身不是自己的真子串)。
模式匹配:在主串T中找(匹配)到第一个与子串p相等的位置。

模式匹配的两种方法

BF算法

在进行模式匹配时采用回溯的方式:
1.从主串的第i个位置开始,与子串的每个字符逐个比较,若均相等,即为模式匹配成功,位置为i
2.若从i开始匹配,但某个位置上不相等,则i就不是要找的位置。这时要将比较的位置回溯到主串的i+1处,子串的比较位置回溯到0,从i+1开始往后再比较。
3.直到匹配成功或i>=length(主串)为止。
每次出现比较不同时,都要进行调整:主串的比较位置回到上次(i)的下一个位置,子串回到0。

BF算法代码

int Index(Sstring T,Sstring p,int pos){
	int i = pos;//主串的开始位置
	int j = 0;//子串的开始位置
	while(i <= T.len && j <= p.len){
		if(T.data[i] == p.data[j]){
			i++;
			j++;
		}else{
			i = i-j+1;
			j = 0;
		}
	}
	if(j > p.len)
		return i-p.len;
	else
		return 0;
}

时间复杂度分析

主串长度为n,子串长度为m
最好情况复杂度O(m),即主串从pos开始的m个字符正好与子串相等,例如:pos=0,主串为abcde,子串为abc,那么刚开始比较就会匹配成功。
最坏情况复杂度O(n*m),每次比较都是到最后一个字符才发现不相等,例如:pos=0,主串为aaaaaaaaaab,子串为aaaab,每次都是比较到子串的最后一位才发现不等,所以要比较n*m次。

KMP算法

不同于BF算法的是,在比较中发现不相等时,主串指针位置不回溯,调整子串指针位置,从而继续进行下一轮比较。

失败函数getNext()

原理:在比较时出现对应位置字符不相等时,在这之前主串和子串对应位置上还都是相等的,即已经部分匹配,利用这个有效信息,设计一种方法使得尽量减少匹配时间,提高效率。具体方法如下:设计一个失败函数,在某个位置出现比对失败(不相等)时,调用失败函数getNext(),在子串p中,该失败位置之前的串中,找到一个最长的既是真前缀又是真后缀的小子串,使子串p调整到小子串最后一个字符的下一个位置,进行下一轮比较。
注意:失败函数只与子串有关,与主串无关。

应用
子串匹配失败时的位置 j01234567
子串字符abaabcac
失败函数计算后的结果next[ ]-10011201

:1.经过失败函数getNext()计算后,所得值为-1时表示,字串的第一个字符就和主串不相等,这时主串的指针位置要后移一格,子串位置归0。
2.失败函数getNext()计算所得值不为-1时,则表示主串指针位置不动,子串指针位置变为所得值,然后继续比较。

失败函数代码
int getNext(char P[], int next[],int m){
	int i = 0,j = 1,k = 1;
	next[0] = -1;
	while(j < m){
		while(k < j){
			if(P[i] == P[k]){
				i++;
				k++;
			}else{
				k = k - i + 1;
				i = 0;
			}
		}
		next[j] = i;
		j++;
		i = 0;
		k = 1;
	}
	return 0;
}

代码原理:既然是找最长且相等的真前缀和真后缀,那就从最长的情况开始逐渐缩小寻找,最长的情况就是:真前缀或真后缀只比串本身少1个字符。
代码分析:那么i就代表每个位置寻找最长真前缀的开始位置,k代表寻找最长真后缀的位置,j则代表某个比对出错的位置,就是在j之前找的真前缀和真后缀。
如果P[i] == P[k]时,那么就看下一个位置。但如果在k没走到j之前就出现不相等的字符了,那么这串在怎么长都没用,因为它不是真后缀
等到k走到j的位置时,i正好位于真前缀的下一个位置,正好next数组值要的就是真前缀的最后一个字符的下一个位置。

时间复杂度分析

主串长度为n,子串长度为m
最好情况复杂度:O(m)。
最坏情况复杂度:O(m+n),匹配时因为不相等时不回溯,所以比较次数可以记为n

函数改进getNextVal

在KMP算法的基础上,考虑到在匹配失败去获取next值时,如果计算所得位置得字符与当前匹配失败的位置上的字符一样,那么在调整到next[i]的位置上时还是会匹配失败,所以nextVal数组考虑这一点,使得所得结果避免出现与原本匹配失败位置相同的字符位置。

int getNextVal(char P[], int nextVal[],int m){
	int i = 0,j = 1,k = 1;
	nextVal[0] = -1;
	while(j < m){
		while(k < j){
			if(P[i] == P[k]){
				i++;
				k++;
			}else{
				k = k - i + 1;
				i = 0;
			}
		}
		while(P[j] == P[i]){
			i = nextVal[i];
		}
		nextVal[j] = i;
		j++;
		i = 0;
		k = 1;
	}
	return 0;
}

改进之处就在,如果发现计算得到的位置i处的字符与比较失败的j处相等的话,即P[j] == P[i],就提前获取位置i处的next值,因为调整过去之后还是会出错,所以提前先获取。
以上均为串相关内容的个人理解,欢迎大家指正!谢谢!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万宝炉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值