4.1 串类型的定义
字符串可以看作是一种特殊的线性表,字符串的数据对象约束为字符集(非数值数据)。
不同的应用所处理的串的特点亦不相同。
在线性表的基本操作中,大多以“单个元素”作为操作对象,而在串中则是以“串的整体”或一部分作为操作对象。
串的定义:
串(String)是零个或多个字符组成的有限序列。
一般记为:s=′a1a2…an′ (n≥0)
s是串的名字, a1a2…an是串s的值
ai可以是字母、数字或其它字符;
串内字符的数目n称为串的长度。
零个字符的串称为空串(null string),其长度为0。
子串:
串中任意个连续的字符组成的子序列称为该串的子串。
主串:
包含子串的串相应地称为主串。
字符的位置:
通常将字符在串中的序号称为该字符在串中的位置。
子串的位置:
以子串的第一个字符在主串中的位置来表示。
两个串相等:
当且仅当两个串的值相等时,称这两个串是相等的,即只有当两个串的长度相等, 并且每个对应位置的字符都相同时才相等。
关于串定义的说明:
串定义中的一对单引号本身不属于串!
其作用避免与变量名或数的常量混淆
空串(null string)和空格串(blank string)是不同的!
为了清晰起见,用符号“ø”表示“空串”
在操作上:
在线性表中,操作的对象基本上都是数据元素;
在串中,操作的对象通常以子串为单位。
例如:查找子串、求取子串、插入或删除子串等。
对于串的基本操作集可以有不同的定义方法,在使用高级程序设计语言中的串类型时,应以该语言的参考手册为准。
在上述抽象数据类型定义的13种操作中,
串赋值 StrAssign、串复制 Strcopy、
串比较 StrCompare、求串长 StrLength、
串联接 Concat 以及求子串 SubString
等六种操作构成串类型的最小操作子集。
串的逻辑结构和线性表极为相似,区别仅在于串的数据对象约束为字符集。
串的基本操作和线性表有很大差别。
在线性表的基本操作中,大多以“单个元素”作为操作对象;
而在串的基本操作中,通常以“串的整体”作为操作对象。
在早期的程序设计语言中,串只是作为输入或输出的常量出现,则只需存储此串的串值,即字符序列即可。之后在多数非数值处理的程序中,串均以变量的形式出现,因此作为一个数据类型也就存在一个存储表示和实现的问题。
4.2 串的表示和实现
1.串的定长顺序存储表示
用一组地址连续的存储单元存储串值的字符序列。这是一种静态存储结构,串值的存储分配是在编译时完成的。因此,需要预先定义串的存储空间大小。
#define MAXSTRLEN 255; //定义能处理的最大的串长度
typedef unsigned char SString[MAXSTRLEN+1]; //0号单元存放串的长度
串的实际长度可在这个预定义长度的范围内随意设定,超过预定义长度的串值则被舍去,称之为“截断”。
串的联接算法
status ConCat(SString S1,SString S2,SString &T){
//用T返回由S1和S2联接而成的新串。若未截断,则返回TRUE,否则FALSE。
if(S1[0]+S2[0]<=MAXSTRLEN){ //未截断
T[1..S1[0]]=S1[1..S1[0]];
T[S1[0]+1..S1[0]+S2[0]]=S2[1..S2[0]];
T[0]=S1[0]+S2[0];
uncut=TRUE;
}
else if (S1[0]
T[1..S1[0]]=S1[1..S1[0]];
T[S1[0]+1..MAXSTRLEN]=S2[1..MAXSTRLEN-S1[0]];
T[0]=MAXSTRLEN;
uncat=FALSE;
}
else { //截断
T[0..MAXSTRLEN]=S1[0..MAXSTRLEN];
//T[0]= = S1[0]= = MAXSTRLEN
uncut = FALSE;
}
return uncut;
}//ConCat
截断不仅在求联接串发生,在其他操作中如插入、置换等也可能发生,要克服这个弊病,惟有不限定串长的最大长度,即动态分配串值的存储空间。
2.串的堆分配存储表示
以一组地址连续的存储单元存放串值字符序列,但它们的存储空间是在程序执行过程中动态分配而得。所以也称为动态存储分配的顺序表。在C语言中,利用malloc()和free()等动态存储管理函数,来根据实际需要动态分配和释放字符数组空间。
---------串的堆分配存储表示---------
typedef struct{
char *ch;
//若是非空串,则按串长分配存储区;否则ch为NULL
int length; //串长度
}HString;
通常,C语言中提供的串类型就是以这种存储方式实现。系统利用动态分配函数malloc()和free()进行串值空间的动态管理,为每一个新生成的串分配一个存储区,称串值共享的存储空间为“堆”,C语言中的串以一个空字符为结束符,串长是一个隐含值。
StrInsert(Hstring &S,int pos,Hstring T){ //在串s中序号为
//pos的字符之前插入串t
if (pos<1 || pos>s->len) return ERROR;
//1≤pos≤StrLength(S) +1, 注意:pos是逻辑定义的下标
if(T.length){ //T非空
if(!(S.ch=(char*)realloc(S.ch, (S.length+T.length)*sizeof(char))))
exit(OVERFLOW);
for(i = S.length-1; i>pos-1; i--) //为插入T而腾出位置
S.ch[i + T.length] = S.ch[i];
S.ch[pos – 1 .. pos + T.length – 2] = T.ch[0.. T.length – 1]; //插入T
/* pos -1 + (T.length – 1) pos + T.length – 2*/
S.length += T.length;
}
return OK
}//StrInsert
3.串的块链存储表示
串值也可以用链表来存储,由于串的数据元素是一个字符,它只有8位二进制数,因此用链表存储时,通常一个结点中存放的不是一个字符,而是一个子串。
存储密度=串值所占的存储位/实际分配的存储位
存储密度小,运算处理方便,但存储占用量大,在进行串处理过程中,内外存交换频繁,影响处理效率。
为了提高存储密度,可使每个结点存放多个字符。通常将结点数据域存放的字符个数定义为结点的大小,显然,当结点大小大于1时,串的长度不一定正好是结点的整数倍,因此要用特殊字符来填充最后一个结点,以表示串的终结。
对于结点大小不为1的链串,其类型定义为只需对上述的结点类型做简单的修改即可。
typedef struct Chunk{
char ch[CHUNKSIZE];
struct Chunk *next;
}Chunk;
typedef struct{//串的链表结构
Chunk *head, *tail;
int curlen;
}LString;
4.3 串的模式匹配
模式匹配:子串定位运算称为模式匹配(Pattern Matching)或串匹配(String Matching)。
模式匹配成功是指在目标串s中找到一个模式串t;
模式匹配不成功则指目标串s中不存在模式串t。
主要讨论串的简单模式匹配算法,采用顺序存储结构。
在串匹配中,一般将主串称为目标串,子串称为模式串。设S为目标串,T为模式串,设:
S=“s0s1s2…sn-1”
T=“t0t1…tm-1”
回顾串匹配(查找)的定义:
Index(S,T,pos)
初始条件:串S和T存在,T是非空串。
操作结果:若主串S中存在和串T值相同的子串,则返回它在主串S中第pos个字符之后第一次出现的位置,否则函数值为0 。
1、简单算法
基本思想:从目标串s的第一个字符起和模式串t的第一个字符比较,若相等则继续逐个比较后续字符,否则从串s的第二个字符起再重新和串t进行比较。依此类推,直至串t中的每个字符依次和串s的一个连续的字符序列相等,则称模式匹配成功,此时串t的第一个字符在串s 中的位置就是t 在s中的位置,否则模式匹配不成功。
假设s=“cddcdc”,t=“cdc”,则模式匹配过程如下:
简单算法的C语言描述如下:
int Index(SString S,SString T, int pos){
//返回子串T在主串S中第pos个字符之后的位置,若不存在,则函数值为0,其中,T非空,(1≦pos≦Strlength(S))。
i=pos; //指向串s的第pos个字符
j=1; //指向串t的第1个字符
while((i<=S[0])&&(j<=T[0])){
if(S[i]==T[j]) { ++i; ++j; } //继续比较后继字符
else {i=i-j+2; j=1;} //串S指针回溯重新开始匹配
}
if(j>T[0]) return i-T[0];
//匹配成功,返回模式串t在串s中的起始位置
else return 0; //匹配失败返回0
} //Index
2、首尾匹配算法
先比较模式串的第一个字符,
再比较模式串的最后一个字符,
最后比较模式串中从第二个到第n-1个字符
具体算法不再详细讨论。
3、KMP算法
标签:子串,字符,存储,串值,pos,length,数据结构,第四章
来源: https://blog.csdn.net/weixin_42260102/article/details/99577162