考研数据结构手记(4)-串

4.1 串的数据类型定义

4.1.1 串的定义

串,即字符串(String),是由零个或多个字符组成的有限序列。
一般记为s = ‘a1a……a,’ (n ≥0)
其中,S是串名,单引号括起来的字符序列是串的值;
a,可以是字母、数字或其他字符;
串中字符的个数n称为串的长度。
n = 0时的串称为空串(用0表示)。
子串:串中任意个连续的字符组成的子序列。
(空串也是字符串的子串)
主串:包含子串的字符串。
字符在主串中的位置:字符在串中的序号
eg: E = ‘Helloworld’,'w’在E中的位置是6.
子串在主串中的位置:子串的第一个字符在主串中的位置。

4.1.2 串和线性表的区别

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

4.1.3 基本操作

假设有串T="",S=“iPhone 11 Pro Max?”,W=“Pro”

StrAssign(&T,chars):赋值操作。把串T赋值为chars。
StrCopy(&T,S):复制操作。由串s复制得到串TStrEmpty(S):判空操作。若s为空串,则返回TRUE,否则返回FALSE
StrLength(S):求串长。返回串s的元素个数。
ClearString(&S):清空操作。将s清为空串。
DestroyString(&S):销毁串。将串s销毁(回收存储空间)
Concat(&T,S1,S2):串联接。用T返回由S1S2联接而成的新串
SubString(&Sub,S,pos,len):求子串。用sub返回串s的第pos个字符起长度为len的子串。
Index(S,T):定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0StrCompare(S,T):比较操作。若S>T,则返回值>0;S=T,则返回值=0;S<T,则返回值<0

eg:
执行基本操作Concat(&T, S, W)后,T="iPhone 11 Pro Max?Pro”
执行基本操作SubString(&T ,S,4,6)后,T=“one 11”
执行基本操作Index(S, W)后,返回值为11

4.2 串的三种存储表示

4.2.1 定长顺序存储结构

类似于线性表的顺序存储结构,用一组地址连续的存储单元存储串值的字符序列。在串的定长顺序存储结构中,为每个串变量分配一个固定长度的存储区,即定长数组。

#define MAXLEN 255      //预定义最大串长为255
typedef struct {
	char ch[MAXLEN];  //每个分盘存储一个字符
	int length;        //串的实际长度
) sstring;

串的实际长度只能小于等于MAXLEN,超过预定义长度的串值会被舍去,称为截断。在一些串的操作(如插入、联接等)中,若串值序列的长度超过上界MAXLEN,约定用“截断”法处理,要克服这种弊端,只能不限定串长的最大长度,即采用动态分配的方式。

4.2.2 堆分配存储结构

typedef struct{
	char *Ch;//按串长分配存储区,ch指向串的基地址
	int length;//串的长度
}HString;      <-----动态数组实现(堆分配存储)
HString S;
S.ch = (char *) malloc (MAXLEN * sizeof(char));
S.length = 0;

利用malloc()为每个新产生的串分配一块实际串长所需的存储空间,若分配成功,则返回一个指向起始地址的指针,作为串的基地址,这个串由ch指针来指示;若分配失败,则返回NULL。已分配的空间可用free()释放掉。

4.2.3 块链存储结构

类似于线性表的链式存储结构,也可采用链表方式存储串值。由于串的特殊性(每个元素只有一个字符),在具体实现时,每个结点既可以存放一个字符,也可以存放多个字符。每个结点称为块,整个链表称为块链结构。
建议采用每个结点存放多个字符的链式存储结构

4.3 串的各种基本操作的实现及应用

4.3.1 求子串

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

4.3.2 比较两个串

StrCompare(S,T):比较操作。
若S>T,则返回值>0;S=T,则返回值=0;S<T,则返回值<0
int StrCompare(SString S, SString T){
	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;
}

4.3.3 定位操作

Index(S,T):定位操作。
若主串S中存在与串T值相同的子串,则返回它在主串S中第一次
出现的位置;否则函数值为0
int Index(SString S, SString T){
	int i=1, n=StrLength(S), m=StrLength(T);
	SString sub;    //用于暂存子串
	while(i<=n-m+1){
		SubString(sub,S,i,m);
		if(StrCompare(sub, T)!=0) ++i;
		else return i; //返回子串在主串中的位置
	}
	return 0;//S中不存在与T相等的子串
} 

4.4 串的模式匹配算法

字符串模式匹配:在主串中找到与模式串相同的子串,并返回其所在位置。

4.4.1 简单的模式匹配算法

主串长度为n,模式串长度为m;
朴素模式匹配算法:将主串中所有长度为m的子串依次与模式串对比,直到找到一个完全匹配的子串,或所有的子串都不匹配为止。
若当前子串匹配失败,则主串指针i指向下一个子串的第一个位置, 模式串指针j回到模式串的第一个位置。
最多对比n-m+1个子串
实现代码:

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; ++j; //继续比较后继字符
		}
		else{
			i=i-j+2;
			j=1;   //指针后退重新开始匹配
		}
	}
	if(j>T. length)
		return i-T. length;
	else
		return 0;
}

最坏时间复杂度:O(mn)
最坏的情况,每个子串都要对比m个字符,共n-m+1个子串,复杂度= o((n-m+1)m) = O(nm)

最好时间复杂度:O(n)
第一个字符就一直都不匹配。

4.4.2 朴素模式匹配算法优化(KMP算法)

如下图:当模式串与主串的前五个字符都匹配时,只有第六个字符不匹配,这个字符之前的主串和模式串保持一致,所以就没有必要检查前面的字符。
在这里插入图片描述
由于前五个字符匹配,所以主串的前五个字符也是abaab,所以可令主串指针i不变,模式串指针j变为3再继续匹配即可,提高了算法效率。
在这里插入图片描述
这种结论对于模式串具有通用性,和主串内容没有关系。
在这里插入图片描述
可以用next数组来表示在不同位置匹配失败时需要修改的j的值。
这个操作主要的实现代码如下:

int Index_KMP(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
			j=next[j];//模式串向右移动
	}
	if(j>T.length)
		return i-T.length;        //匹配成功
	else
		return 0;
}

4.4.3 KMP算法求next数组

next数组的作用:当模式串的第j个字符失配时,从模式串的第next[ij]的继续往后匹配。

例一:
在这里插入图片描述
任何模式串都一样,第一个字符不匹配时,只能匹配下一个子串,因此,next[1]永远都是0,next[2]永远都是1,next[3]也是1,next[4]也是1,next[5]是2,next[6]是1:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值