大话数据结构第五章笔记

本文介绍了串的概念,包括串的定义、长度、空串和子串的定义。接着讲解了串的比较方式,并探讨了串的顺序存储结构和链式存储结构的特点。重点讨论了KMP模式匹配算法,解释了KMP的核心思想和next数组的生成,分析了算法的时间复杂度,并提及了算法的改进方法。

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

5.2
1.串是由零个或多个字符组成的有限序列,又名叫字符串。
2.串中的字符数目n称为串的长度。
3.零个字符的串称为空串,它的长度为0,也可以直接用双引号""直接括起来。
4.空格串,是包含空格的串。空格串是有长度的,而且可以不止一个空格。
5.子串与主串,串中任意个数的连续字符组成的子序列称为该串的子串,包含子串的串称为主串。
6.子串在主串中的位置就是子串的第一个字符在主串中的序号

5.3串的比较:
1.将两个字符串都从左至右对齐,然后开始比较,字符串的数量可以不同,比较字符串时一一对应,按照ascii码的方式,字母在前的小,到第一个不同字母比较完之后就完。

5.4串的数据结构:

ADT 串(string)
Data 
	串中元素仅有一个字符组成,相邻的元素具有前驱和后继关系
Operation
	StrAssign (T,*chars): 生成一个其值等于字符串的常量chars的串T
	StrCopy (T,S):串S存在,由串S复制得到串
	ClearString(S): 串s存在,将串清空
	StringEmpty(S):若串S为空,返回true,否则返回false
	StrLength(S):返回串中的元素个数,即串的长度
	StrCompare(S,T):若S>T,返回值>0,若S=T,返回0,若S<T,返回值<0
	Concat(T,S1,S2):用T返回又S1和S2链接而成的新串
	SubString (Sub,S,pos,len):  串s存在,1<=pos<=StrLength(S),0<=len<=StrLength(S)-pos+1,用Sub返回串S的第pos个字符起长度为len的子串
	Index (S,T,pos): 串S和T存在,T是非空串, 1<=pos<=StrLength(S)
					若主串S中存在和串T值相同的子串,则返回它在主串S中
					 第pos个字符之后第一次出现的位置,否则返回0
	Replace(S,T,V):  串S,T和V存在,T是非空串。用V替换主串S中出现的所有与T相等的不重复的子串
	StrInsert(S,pos,T):  串S和T存在,1<=pos<=Strlength(S)+1
						在串S的第pos个字符之前插入串T
	Strlength(S,pos,len): 串S存在,1<=pos<=Strlength(S)-len+1
						`  从串S中删除第pos个字符起长度为len的子串
endADT

5.4.1index的操作(取主串中,与子串相同的字符串的第一个字符的位置)

/*T为非空串。若主串s中第pos个字符之后存在于T相等的子串*/
/*则返回第一个这样的子串在S中的位置,否则返回0*/
int Index(String S, String T, int pos)
{
	int n,m,i;
	String sub;
	if(pos>0)
	{
		n=StrLength(S);
		m=Strlength(T);
		i=pos;           //刚开始这个pos不是要所要的位置,不知道也不必说
		while(i <= n-m+1)   //见下图有这代码的解释
		{
			SubString(sub,S,i,m);     //取主串第i个位置
										//长度与T相等子串给sub
			if(StrCompare(sub, T) !=0 )   //如果两个串不相等
				++i;
			else                  //如果两串相等
				return i;  			//则返回i值
		}
		
	}
	return 0;    //若无子串与T相等,返回0
}

在这里插入图片描述
串的顺序存储结构:
在这里插入图片描述
串的链式存储结构:没有占满的位置,可以用其他非字符替代
在这里插入图片描述

5.6朴素的模式匹配法:

//返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0
//T非空,1<=pos<=Strlength(S)
int Index(String S, String T, int pos)
{
	int i=pos;        //i主要用于主串s中当前位置的下标,若pos不为1
						//则从pos位置开始匹配
	int	j=1;					 //j用于子串T中当前位置下标值
	while(i <= S[0] && j <= T[0])    //若i小于s长度且j小于T的长度时循环
	{
		if(S[i] == T[j])      //两个字母相等则继续
		{
			++i;
			++j;
		}
		else        //指针后退重新开始匹配
		{
			i=i-j+2;    //i退回到上次匹配首位的下一位,详细看下图1处
			j=1;     //j退回到子串T的首位
		}
	}
	if(j>T[0])   //上面的while结束有两种情况,i>S[0] 或者  j>T[0] 当i>S[0]一定没有对应的字符串匹配,当j>T[0]时,表示走完了字串,找到了相对应的
		return i-T[0];      //详细看下图2处
	else
		return 0;
}

在这里插入图片描述
朴素算法的时间复杂度:
在这里插入图片描述
在这里插入图片描述

5.7KMP模式匹配法
1.kmp算法的普遍理解:如下图,kmp算法的使用前提是T串中的首字符a与后面的均不相等,由于a到e子串T与主串S都对应相同,所以下一次直接比较a与f即可,这便是kmp的引入
在这里插入图片描述
2.实际上,j的回溯位置的变化与主串S没有什么关系,主要是看子串T中是否有相同的字符,如下图中不用在比较a,b,
j从6回溯到j=3
在这里插入图片描述
关于j的回溯的数组next的原理:如下图
在这里插入图片描述
这里对于5这个位置下要看前4个字符,第一个a与最后一个a相同,相同字母只有一个,所以5下就写2
这里对于6这个位置下要看前5个字符,前两个字符ab与最后两个字符ab完全相同,所以6下就写3
如下图:
在这里插入图片描述
j回溯位置下标的数组:

//通过计算返回子串T的next数组
void get_next (String T, int *next)
{
	int i,j;
	i=1;             
	j=0;               
	next[1]=0;
	while(i<T[0])        //此处T[0]表示串的长度
	{
		if(j==0 || T[i]==T[j])
		{
			++i;
			++j;
			next[i]=j;       //将j回溯的位置保存到next中
		}
		else
			j=next[j];          //j回溯到对应位置
	}
}

下面是KMP的整体代码:

//返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0
int Index_KMP(String S, String T, int pos)
{
	int i=pos;       //i就是那里的代码值不同就从哪里开始,如图5-7-3 i为6就从6开始
	int j=1;       //j用于当前位置中下标值
	int next[255];    //定义一next数组
	get_next(T,next);   //对串做分析,得到next数组
	while(i <= s[0] && j <= T[0])   //若i小于s的长度且j小于且小于T的长度时,循环继续
	{
		if(j==0 || s[i]==T[j])    //两个字母相等则继续,相对于朴素算法增加了j=0判断
		{
			++i;
			++j;
		}
		else
		{
			j=next[j];    //如果字母不相同,则j的值回溯
		}
	}
	
//后面两行代码和朴素算法相同
	if(j>T[0])    
		return i-T[0];
	else
		return 0;
	
}

kmp算法的时间复杂度分析:
在这里,创建next数组时间复杂度为o(m),后面的时间复杂度为o(n),则整体的时间复杂度为o(m+n)
kmp的核心思路:如果子串中一前一后有相同的字符,后面的字符不管与主串中是相同还是不同,对于前面的字符在往后走时不用在与之前

5.7.4kmp模式匹配算法的改进

//求模式串T的next函数修正值并存入数组nextval,整个nextval还是以计算next的方法来构建,区别在于数值不同,目的就是减少j的回溯次数,以减少复杂度
void get_nextval(String T, int *nextval)
{
	int i,j;
	i=1;
	j=0;
	nextval[1]=0;
	while(i<T[0])           //T[0]表示串的长度
	{
		if(j==0 || T[i]==T[j])       //T[i]表示后缀的单个字符,T[j]表示前缀的单个字符
		{
			++i;
			++j;
/* 这里的代码是改进后不同于之前的next算法
			if(T[i]!=T[j])   //若当前字符与前缀字符不同
				nextval[i]=j;		//就将next中的值给nextval
			else
				nextval[i]=nextval[j];  //字符相同,nextval与之前相同字符所对应的nextval值相同
*/
		}
		else
			j=nextval[j];      //相当于计算next数组中的值,只是改了个名,就这样理解
	}
}

5.7.4 kmp算法的改进
1.相对于之前的kmp算法j的回溯位置变化太多,而改进后,j回溯的变化位置不多,这里应该是T中有太多连续字符相同的情况下
next数组中,数字不同的程度与j回溯到不同位置的次数成正比,数字越复杂,回溯的次数越多,时间复杂度越大。
2.nextval[]数组变化是:第一数组值为0,往后每一个数组值,如果串中的字符与next数组中对应位置的字符相同,则nextval中的值也与对应位置的nextval相同,如果不同则nextval值就是next值
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值