《数据结构》天勤和王道 第四章 串

天勤部分

1. 串的基础

1.1 逻辑结构

在这里插入图片描述

1.2 存储结构

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1.3 赋值操作

int strAssign(Str& str,char* ch){
	if(str.ch){       //如果串中已经有元素就将其释放掉 
		free(str.ch);
	}
	int len=0;
	char *c=ch;
	while(*c){        //统计字符串ch的长度 
		++len;
		++c;
	}
	if(len==0){       //即使传进来的是空串,也可以进行赋值操作 
		str.ch=NULL;
		str.length==0;
		return 1; 
	}else{
		str.ch=(char*)malloc(sizeof(char)*(len+1));    //长度包括结束符 
		if(str.ch==NULL){      //如果分配空间失败,则返回0 
			return 0;
		}else{
			c=ch;
			for(int i=0;i<=len;++i,++c){   //把ch的结束符也赋进去 
				str.ch[i]=*c;
			}
			str.length=len;    
			return 1;
		}
	}
} 

1.4 串比较

在这里插入图片描述

int strCompare(Str s1,Str s2){
	for(int i=0;i<s1.length&&i<s2.length;++i){
		if(s1.ch[i]!=s2.ch[i]){
			return s1.ch[i]-s2.ch[i];
		}
	}
	return s1.length-s2.length;
}

1.5 串连接

int concat(Str& str,Str str1,Str str2){
	if(str.ch){
		free(str.ch);
		str.ch=NULL;
	}
	str.ch=(char*)malloc(sizeof(char)*(str.length+str2.length+1));
	if(!str.ch){
		return 0;
	}
	int i=0;
	while(i<str1.length){
		str.ch[i]=str1.ch[i];
		++i;
	}
	int j=0;
	while(j<=str2.length){
		str.ch[i+j]=str.ch[j];
		++j;
	}
	str.length=str1.length+str2.length;
	return 1;
}

1.6 求子串

int subString(Str& substr,Str str,int pos,int len){   //pos为子串的起始下标,len为子串的长度 
	if(pos<0||pos>=str.length||len<0||len>str.length-pos){
		return 0;
	}
	if(substr.ch){
		free(substr.ch);
		substr.ch=NULL;
	}
	if(len==0){        //子串也可取空串 
		substr.ch=NULL;
		substr.length=0;
		return 1;
	}else{
		substr.ch=(char*)malloc(sizeof(char)*(len+1));
		int i=pos;
		int j=0;
		while(i<pos+len){
			substr.ch[j]=str.ch[i];
			++i;
			++j;
		}
		substr.ch[j]='\0';
		substr.length=len;
		return 1;
	}
}

1.7 清空串

int clearString(Str& str){
	if(str.ch){     //如果串非空则清空 
		free(str.ch);
		str.ch=NULL;
	}
	str.length=0;
	return 1;
}

2. KMP算法手工求解next数组

KMP算法是为了从主串中快速地找到想要的子串。

当扫描到这种,一般情况下,会把指针回溯指向主串的第二个元素。
在这里插入图片描述
如这样:
在这里插入图片描述
但是这样造成效率低下,所以KMP就为了来解决这个问题。KMP可以仅仅比较模式串,并且让指针不回溯。
在这里插入图片描述
当指针之前与主串匹配时,此时模式串中有公共前后缀AB。就可以直接移动模式串到前缀与后缀一样的位置上继续进行比较。
在这里插入图片描述

找公共前后缀时的条件是找最长的且长度要小于当前指针指向元素前面的子串的长度的公共前后缀。

这里模式串下标从1开始,不过具体可以看题目要求。
在这里插入图片描述
当扫描到六号位时,此时指针前面的子串的最大公共前后缀是ABA,长度为3。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. KMP算法代码

在这里插入图片描述

//求next数组的代码
void getNext(Str substr,int next[]){
	int j=1,t=0;
	next[1]=0;   //无脑写0
	while(j<str.length){     //注意这里不能是小于等于,因为①语句j+1会越界    
		if(t==0||substr.ch[j]==substr.ch[t]){
			next[j+1]=t+1;   //① 
			++t;
			++j;
		}else{
			t=next[t];
		}
	}	
} 
//KMP算法 
int KMP(Str str,Str substr,int next[]){   //str为主串,substr为模式串 
	int i=1,j=1;
	while(i<=str.length&&j<=substr.length){
		if(j==0||str.ch[i]==substr.ch[j]){
			++i;
			++j;
		}else{
			j=next[j];
		}
	} 
	if(j>substr.length){
		return i-substr.length;
	}else{
		return 0;
	}
} 

4. 求解nextval数组及代码

在这里插入图片描述
直接拿上面求next数组的代码进行修改。

void getNextval(Str substr,int nextval[],int next[]){
	int j=1,t=0;
	next[1]=0;   //无脑写0
	nextval[1]=0;
	while(j<str.length){     //注意这里不能是小于等于,因为①语句j+1会越界    
		if(t==0||substr.ch[j]==substr.ch[t]){
			next[j+1]=t+1;   //①
			if(substr.ch[j+1]!=substr.ch[next[j+1]]){
				nextval[j+1]=next[j+1];
			}else{
				nextval[j+1]=nextval[next[j+1]];
			} 
			++t;
			++j;
		}else{
			t=nextval[t];
		}
	}	
} 

再精简一下:

void getNextval(Str substr,int nextval[]){
	int j=1,t=0;
	nextval[1]=0;
	while(j<str.length){       
		if(t==0||substr.ch[j]==substr.ch[t]){
			if(substr.ch[j+1]!=substr.ch[next[j+1]]){
				nextval[j+1]=t+1;
			}else{
				nextval[j+1]=nextval[t+1];
			} 
			++t;
			++j;
		}else{
			t=nextval[t];
		}
	}	
} 

王道的部分

1. 串的定义和基本操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 串的存储结构

2.1 串的顺序存储

在这里插入图片描述
在这里插入图片描述

2.2 串的链式存储

在这里插入图片描述

2.3 基本操作的实现

在这里插入图片描述

2.3.1 求子串

在这里插入图片描述

2.3.2 比较操作

在这里插入图片描述
这个跟天勤的一样,不懂可以看上面的。

2.3.3 定位操作

在这里插入图片描述
在这里插入图片描述

2.4 朴素模式匹配算法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5 KMP算法

在这里插入图片描述
朴素模式匹配算法在每次匹配失败后,指向主串的指针总要回溯。影响了总体效率,所以有了KMP算法。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意一下当第一个元素就匹配失败的情况
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
代码如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.6 KMP算法 求next数组(手算)

这个有两种方法:一种是王道讲的这个;一种是天勤上面讲的,根据当前要判断的元素的前面的模式串的子串中公共前后缀的长度加1来确定当前next数组的值(不过要注意模式串下标,有的题目是j从0开始,然后这里需要加1是因为next数组第一个是0,第二个是1)。
在这里插入图片描述
在这里插入图片描述
例如当第三个元素不匹配时,看模式串前两个元素,公共前后缀长度为0,加1就是1,所以next[3]=1。
在这里插入图片描述
在这里插入图片描述
当第四个元素不匹配时,此时模式串前三个元素的公共前后缀是“a”,长度是1,所以加1后是2,即next[4]=2。以此类推。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.7 next数组的优化(nextval数组)

举个例子:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
KMP算法不用改变,只是优化next数组,形参也使用优化后的nextval数组。

上面的描述可能有点绕,可以看下面的例子:
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值