数据结构(第四章)——串

一、定义与基本操作

定义:串,即字符串(String),是由零个或者多个字符组成的有限序列,一般记为S=‘a1a2…an’(n$\geq$0),n=0时称为空串

字符集:英文字符——ASCII字符集;中英文——Unicode字符集(编码方案UTF-8、UTF-16等)

注:采用不同的编码方案,每个字符所占空间不同,考研中默认每个字符占用1B即可

串与线性表的对比

  • 串是一种特殊的线性表,数据结构之间呈线性关系
  • 串的数据对象限定为字符集(如中文字符、英文字符、数字字符、标点字符等)
  • 串的基本操作:比如增删改查通常以字串为操作对象

串的基本操作:假设有串T="",S=“Haaaaax”,W=“HaHahahha”

  1. StrAssign(&T,chars);——赋值操作:把串T赋值为chars
  2. StrCopy(&T,S);——复制操作:由串S复制得到串T
  3. StrEmpty(S);——判空操作:若S为空串,则返回true,否则返回false
  4. StrLength(S);——判空操作:若S为空串,则返回true,否则返回False
  5. ClearSting(&S);——清空操作:将S清为空串
  6. DestoryString(&S);——销毁串:将串S销毁(回收存储空间)
  7. Concat(&T,S1,S2);——串连接:用T 返回由S1和S2连接而成的新串
  8. SubString(&Sub,S,pos,len);——求子串:用Sub返回串S的第pos个字符起长度为len的字串(从1开始数)
  9. Index(S,T);——定位操作:若子串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置,否则函数值返回为0
  10. StrCompare(S,T);——比较操作:若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0(ASCII码)

二、存储结构

分为顺序存储和块链存储(链式存储)

顺序存储

  • 静态数组(定长的顺序存储)
  • 动态数组(堆分配存储)
// 串的顺序存储
#define MAXLEN 255
typedef struct{
	char ch[MAXLEN]; // 每个分量存储一个字符
	int length; // 串的实际长度
}SString;
// 串的动态数组实现(堆分配存储)
#define MAXLEN 255
typedef struct{
	char *ch;
	int length;
}HString;

// 使用方式
int main(){
	HString S;
	S.ch=(char *)malloc(MAXLEN*sizeof(char));
	S.length=0;
}

串的部分基本操作算法实现:

// 基本操作的实现
// 求子串
bool SubString(SString &Sub,SString S,int pos,int len){// 用SUb返回串S的第pos个字符串起长度为len的子串
	if(pos+len>S.length){ //长度或者pos越界
		return false;
	}
	for(int i=pos;i<pos+len;i++){
		Sub.ch[i-pos+1] = S.ch[i];
	}
	Sub.length=len;
	return false;
}

// 比较
int StrCompare(SString S,SString T){ // 比较操作,如果S>T,则返回数值》0,S=T,返回数值=0,S<T返回数值<0
	for(int i=1;i<S.length&&i<T.length;i++){
		if(S.ch[i]!=T.ch[i])
			return S.ch[i]-T.ch[i];
	}
	return S.length-T.length;
}


// 定位
int Index(SString S,SString T){
	int i=1,n=StrLength(S),m=StrLength(T); //StrLength(T)——求串长
	SString sub;  // 用于暂存子串
	while(i<=n-m+1){
		SubString(sub,S,i,m);
		if(StrCompare(sub,T)!=0) 
			++i;
	}
	return 0;
}

串的链式存储

存储方式(使用下面的方式)每个结点存放多个字符,没有字符的位置用’#‘或者’\0’补足

// 串的链式存储
// 块链存储方式1:
typedef struct StringNode{
	char ch; //每个结点存一个字符
	struct StringNode *next;
} StringNode,*String;

// 块链存储方式2:提高存储密度,最后一个块不足的用'#'或者'\0'表示
typedef struct StringNode{
	char ch[4]; //每个结点存4个字符
	struct StringNode *next;
} StringNode,*String;

三、串的应用——字符串模式匹配算法

被搜索的串为主串,需要搜索的串为模式串(与主串不同,模式串不一定能在主串中找到)
字符串模式匹配:在主串中找到模式相同的字串,并返回其所在位置

朴素模式匹配算法

下边的算法:设主串⻓度为 n,模式串⻓度为 m,则:

  • 最坏时间复杂度 : O(nm);
  • 最好时间复杂度:最好的情况,每个⼦串的第⼀个字符就匹配失败,共 n-m+1 个⼦串,复杂度 = O(n-m+1) = O(n)

朴素模式匹配算法:将主串中所有⻓度为m的⼦串(最多有n-m+1个)依次与模式串对⽐,直到找到⼀个完全匹配的⼦串,或所有的⼦串都不匹配为⽌。

  1. 比如上边的定位操作Index(S,T)的代码实现逻辑
  2. 常用方式:直接使用数组下标 当前字串匹配失败i=i-j+2; j=1;匹配成功:i-T.length,暴力解决问题,如下:
// 字符串的模式匹配
// 朴素模式匹配算法
int Index(SString S,SString T){
	int i=1,j=1; // i记录被对比的字符串的位序,j记录需要对比的字符串的位序
	while(i<=S.length && j<=T.length){
		if(S.ch[i]==T.ch[i]){
			++i;
			++j;
		}else{
			i=j-i+2; // 对比失败,回到这一次对比的初始元素的下一个位置
			j=1; // j回到T的第一个位置,再次开始对比
		}
	}
	if(j>T.length){ //字符串对比成功?
		return i-T.length; //返回T在S中开始的位置
	}else
		return 0;
}

KPM算法

//KMP算法
// 核心思想——主串指针不回溯
// 主要操作,求出next[]数组
// next[]数组的求法需要用到字符串的前缀、后缀和部分匹配值——需要事先求出
int Index_KMP(SString S,SString T,int next[]){
	int i=1,j=1; // i记录被对比的字符串的位序,j记录需要对比的字符串的位序
	while(i<=S.length && j<=T.length){
		if(j==0||S.ch[i]==T.ch[i]){ 
			++i;
			++j;
		}else{
			j=next[j]; // next数组中的next[j]记录的是匹配到第j个位置时候匹配失败,则j需要移动到的位置
		}
	}
	if(j>T.length){ //字符串对比成功?
		return i-T.length; //返回T在S中开始的位置
	}else
		return 0;
}
next数组求解

next数组的作用:当模式串的第j个字符失配时,从模式串的第nextj]的继续往后匹配
任何模式串都一样,第一个字符不匹配时,只能匹配下一个子串,因此,往后余生,next[1]==0(如果j=0,下一步i++;j++;)
任何模式串都一样,第二个字符不匹配时,应尝试匹配模式串的第一个字符,因此,往后余生,next[2]==1
在不匹配的位置前边,画一条美丽的分界线,模式串一步一步往后退,直到分界线之前“能对上”,或者模式串完全跨过分界线为止
此时j指向哪儿,next数组就是多少
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值