数据结构复习笔记 第四章 串

串的定义及基本操作

串的定义

串,即字符串(String)是由零个或多个字符组成的有限序列。

串vs线性表

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

串的基本操作

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

串的比较操作

比较函数:
StrCompare(S,T):比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。
比较方式:
从第一个字符开始往后依次对比,先出现更大字符的串就更大:“abandon” < “aboard”;
长串的前缀与短串相同时,长串更大:“abstract” < “abstraction”;
只有两个串完全相同时,才相等:“academic”=“academic”。

串的存储结构

顺序存储

在这里插入图片描述

#define MAXLEN 255  //预定义最大串长为255

//静态数组实现(定长顺序存储):
typedef struct{
	char ch[MAXLEN];//每个分量存储一个字符
	int length;//串的实际长度
}SString;

//动态数组实现(堆分配存储):
typedef struct{
	char *ch;//按串长分配存储区,ch指向串的基地址
	int length;//串的长度
}HString;
HString S;

S.ch = (char *) malloc(MAXLEN * sizeof(char));
S.length = 0;
//用完需要手动free

顺序存储的几种存储方案
在这里插入图片描述
通常采用方案四。

链式存储

每个结点存1个字符:

typedef struct StringNode{
	char ch; 
	struct StringNode * next;
}StringNode, * String;

在这里插入图片描述
每个结点存多个字符

typedef struct StringNode{
	char ch[4]; 
	struct StringNode * next;
}StringNode, * String;

在这里插入图片描述

基本操作的实现

采用静态数组,方案四:

#define MAXLEN 255  //预定义最大串长为255
//静态数组实现(定长顺序存储):
typedef struct{
	char ch[MAXLEN];//每个分量存储一个字符
	int length;//串的实际长度
}SString;

SubString(&Sub,S,pos,len):求子串

//求子串
bool SubString(SString &Sub, SString S, int pos,int len){
	//子串范围越界
	if (pos+1en-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;
}

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; 
}

Index(S,T):定位操作

//定位操作
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相等的子串
}

朴素(简单)模式匹配算法

其实就是定位操作:

int Index(SString S,SString T){
	int k=1;//当前所对比的子串的起始位置
	int i=k, j=1;//分别指向子串和模式串的对应位置
	while(i<=S.length && j<=T.length){
		if(s.ch[i]= =T.ch[j]){
			++i;
			++j;//继续比较后继字符
		} 
		else{
			k++;//检查下一个子串
			i=k;
			j=1;
		}
	}
	if(j>T.length)
		return k;
	else
		return 0;
}

在这里插入图片描述
若模式串长度为m,主串长度为n:
匹配成功最好时间复杂度为O(m)
匹配失败最好时间复杂度为O(n-m+1)=O(n-m)≈O(n);(通常情况下,n远大于m)
匹配成功/失败最多需要(n-m+1)*m次比较,所以最坏时间复杂度为O(nm)

以下为王道课本上的代码:

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;//注意此处直接用i和j来表示
			j=1;//指针后退重新开始匹配
		}
	}
	if(j>T.length)
		return i-T.length;
	else
		return 0;
}

KMP算法-朴素模式匹配算法的优化

朴素模式匹配算法的缺点:
当某些子串与模式串能部分匹配时,主串的扫描指针 i 经常回溯,导致时间开销增加
**改进思路:**主串指针不回溯,只有模式串指针回溯
优化情况分析:
在这里插入图片描述

在这里插入图片描述

若当前两个字符匹配,则 i++,j++;
若 j=1 时发生不匹配,则应让 i++,而 j 依然是1(先令j=0,再进行 j++);
若 j=2 时发生不匹配,则应让 j 回到 1;
若 j=3 时发生不匹配,则应让 j 回到 1;
若 j=4 时发生不匹配,则应让 j 回到 1 ;
若 j=5 时发生不匹配,则应让 j 回到 2 ;
若 j=6 时发生不匹配,则应让 j 回到 1。

KMP算法代码

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;
}

KMP算法-求next数组

概念:

next 数组:当模式串的第 j 个字符匹配失败时,令模式串跳到第next[j]个字符的再继续匹配;
串的前缀:包含第一个字符,且不包含最后一个字符的子串
串的后缀:包含最后一个字符,且不包含第一个字符的子串
当第j个字符匹配失败,由前 1~j-1个字符组成的串记为S。
S的最长相等前后缀长度
例:‘aba’的前缀有:‘a’、‘ab’,后缀有:‘a’、‘ba’,所以‘aba’的最长相等前后缀长度为1;同理‘abab’的最长相等前后缀长度为2;‘ababa’的最长相等前后缀长度为3…
next[j]= S的最长相等前后缀长度 +1
特别地,next[1]=0

例如:当S=‘ababaa’时,

序号j123456
模式串ababaa
next[ j ]011234

当S=‘aaaab’时,

序号j12345
模式串aaaab
next[ j ]01234

算法代码

KMP算法:当子串和模式串不匹配时,主串指针 i不回溯,模式串指针 j=next[j]
算法平均时间复杂度:O(n+m)

//求模式串T的next数组
void get_next(SString T,int next[]){
	int i=1,j=0;
	next[1]=0;
	while(i<T.length){
		if(j==0||T.ch[i]==T.ch[j]){
			++i;++j;
			//若pi=pj,则next[j+1]=next[j]+1
			next[i]=j;
		}
		else
			//否则令j=next[j],循环继续
			j=next[j];
	}
}
//KMP算法
int Index_ _KMP(SString S,SString T){
	int i=1, j=1;
	int next [T.length+1];
	get_next(T,next); //求模式串的next数组
	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;
}

KMP算法优化—nextval数组

KMP算法存在的问题:
在这里插入图片描述
j=4时,gl比较,不等,下一步是j=1,但j=1时依然是gl比较,就进行了一次无意义的对比。
就此引入nextval[j]数组:
在这里插入图片描述
nextval数组的求法:

  1. 先算出next数组
  2. 先令nextval[1]=0
for (int j=2; j<=T.length; j++) {
	if(T.ch[next[j]]==T.ch[j])
		nextval[j]=nextval[next[j]];
	else
		nextval[j]=next[j];
}

KMP算法优化:当子串和模式串不匹配时j=nextval[j];

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值