4.1 串类型的定义

(1)串(或字符串)串是由零个或多个字符组成的有限序列 ,记作
S = ‘c1c2c3…cn’ (n>=0)
其中,S是串名字,‘c1c2c3…cn’是串值
ci是串中字符
n是串的长度,表示串中字符的数目。
空串:零个字符的串称为空串, 记作 “Ø”
2)子串、主串
子串:串中任意个连续的字符组成的子序列
主串:包含子串的串
例如:假设a、b、c、d为如下4个串
a=‘BEI’ b=‘JING’
c=‘BEIJING’ d=‘BEI JING’
注意:空串是任意串的子串,任一串是它自身的子串。除它本身外,一个串的其它子串都是它的真子串
(3)串相等:两个串的长度相等,并且各个对应位置的字符都相等时才相等。
4)空格串:由一个或多个空格组成的串。
空串和空格串的区别:空串是长度为零的串,空格串是仅包含空格字符的串,长度不为零。
串与线性表的区别:
(1)数据对象:串的数据对象约束为字符集
(2)串的操作:线性表大多以“单个元素”作为操作对象;串通常以“串的整体”作为操作对象。
(5)串的抽象数据类型定义
ADT String {
数据对象:D ={ai | ai∈Characterset,(i=1,2,…,n, n≥0)}
数据关系:R1 = {<ai-1,ai>|ai-1,ai ∈ D,(i=2,3,…,n)}
基本操作:
StrAssign (&T, chars) DestroyString(&S)
StrCopy (&T, S) StrLength(S)
StrCompare (S, T) Concat (&T, S1, S2)
StrEmpty (S) SubString (&Sub, S, pos, len)
Index (S, T, pos) Replace (&S, T, V)
StrInsert (&S, pos, T) StrDelete (&S, pos, len)
ClearString(&S)
} ADT String
6)串的基本操作
1) StrAssign (&T, chars)
初始条件:chars 是字符串常量。
操作结果:把 chars 赋为 T 的值。
2) StrCopy (&T, S)
初始条件:串 S 存在。
操作结果:由串 S 复制得串 T。
3) DestroyString (&S)
初始条件:串 S 存在。
操作结果:串 S 被销毁
4) StrEmpty (S)
初始条件:串 S 存在。
操作结果:若 S 为空串,则返回true,否则返回 false。
5) StrCompare (S, T)
初始条件:串 S和T存在。
操作结果:若S > T,则返回值> 0; 若S = T,则返回值= 0; 若S < T,则返回值< 0。
例如:StrCompare(‘data’, ‘state’) < 0
StrCompare(‘cat’, ‘case’) > 0
6) StrLength (S)
初始条件:串 S 存在。
操作结果:返回 S 的元素个数,称为串的长度。
7) Concat (&T, S1, S2)初始条件:串 S 1和S2存在。
操作结果:用T返回由S1和S2联接而成的新串。
例如: Concat( T, ‘man’, ‘kind’)
求得 T = ‘mankind’
8) SubString (&Sub, S, pos, len)
初始条件:串 S 存在,1≤pos≤StrLength(S)
且 0≤len≤StrLength(S)-pos+1。
操作结果:用Sub返回串S的第 pos 个字符起长度为len的子串。
例如:
SubString( sub, "commander ", 4, 3)
求得 sub = "man " ;
9) Index (S, T, pos)
初始条件:串S和T存在,T是非空串,1≤pos≤StrLength(S)。
操作结果:若主串 S 中存在和串 T 值相同的子串, 则返回它在主串 S 中第pos 个字符之后第一次出现的位置;否则函数值为0。
子串在主串中的位置”指子串中的第一个字符在主串中的位序。
例如:假设 S = "abcaabcaaabc ", T = "bca "
Index(S, T, 1) = 2
Index(S, T, 3) = 6
Index(S, T, 8) = 0
10) Replace (&S, T, V)初始条件:串S, T和 V 均已存在, 且 T 是非空串。
操作结果:用V替换主串S中出现的所有与 (模式串)T相等的不重叠的子串。
11) StrInsert (&S, pos, T)初始条件:串S和T存在, 1≤pos≤StrLength(S)+1。
操作结果:在串S的第pos个字符之前插入串T。
例如:S = "chater ",T = "rac ",
则执行 StrInsert(S, 4, T) 之后得到S = "character "
12) StrDelete (&S, pos, len)初始条件:串S存在 1≤pos≤StrLength(S)-len+1。
操作结果:从串S中删除第pos个字符起长度为len的子串。
13) ClearString (&S)初始条件:串S存在。
操作结果:将S清为空串。
6)串的基本操作
对于串的基本操作集可以有不同的定义方法,在使用高级程序设计语言中的串类型时,应以该语言的参考手册为准。
例如:C语言函数库中提供下列串处理函数:
gets(str) 输入一个串;
puts(str) 输出一个串;
strcat(str1, str2) 串联接函数;
strcpy(str1, str2) 串复制函数;
strcmp(str1, str2) 串比较函数;
strlen(str) 求串长函数;
在上述抽象数据类型定义的13种操作中,
串赋值StrAssign、串复制Strcopy、
串比较StrCompare、求串长StrLength、
串联接Concat以及求子串SubString
等六种操作构成串类型的最小操作子集。
即:这些操作不可能利用其他串操作来实现,
反之,其他串操作(除串清除ClearString和 串销毁DestroyString外)可在这个最小操作子 集上实现。
例如,可利用串比较、求串长和求子串等操作实现定位函数 Index( S, T, pos )。
int Index (String S, String T, int pos) {
// T为非空串。若主串S中第pos个字符之后存在与 T相等的子串,
// 则返回第一个这样的子串在S中的 位置,否则返回0
if (pos > 0) {
n = StrLength(S); m = StrLength(T); i = pos;
while ( i <= n-m+1) { //1.循环
SubString (sub, S, i, m); //2. 子串
if (StrCompare(sub,T) != 0) ++i ; //3. 比较
else return i ; //S中从第i个字符之后存在与T相等的子串
} // while
} // if
return 0; // S中不存在与T相等的子串
} // Index
在这里插入图片描述

void replace(String& S, String T, String V) {
n=StrLength(S); m=StrLength(T); pos = 1;
StrAssign(news, NullStr); i=1;
while ( pos <= n-m+1 && i) { 1.循环
i=Index(S, T, pos); //2.在S中定位子串T的位置i,
if (i!=0) { //定位成功
SubString(sub, S, pos, i-pos); // 3. 取S中保留的子串
Concat(news, news,sub); // 4. 连接
Concat(news, news, V);
pos = i+m; //5.下一次定位的位置
}//if
}//while
SubString(sub, S, pos, n-pos+1); // 剩余串
Concat( S, news, sub );
}//replace

4.2 串的表示和实现

1、定长顺序存储表示
静态分配:为每个串预先分配一个固定长度的存储区域。
用定长数组描述: #define MAXSTRLEN 255 //最大串长
typedef unsigned char SString[MAXSTRLEN + 1]
//0号单元存放串的长度
1、定长顺序存储表示
实际串长可在所分配的固定长度区域内变动
以下标为0的数组分量存放串的实际长度;
在这里插入图片描述
1)串的连接
tatus Concat(SString &T, SString S1, SString S2) {
// 用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] <MAXSTRSIZE) { // 截断
T[ 1,…,S1[0] ] = S1[ 1,…,S1[0] ];
T[ S1[0]+1,…,MAXSTRLEN ] =S2[ 1,…,MAXSTRLEN-S1[0] ];
T[0…MAXSTRLEN] = S1[0…MAXSTRLEN];
T[0] = MAXSTRLEN;
uncut = FALSE;
return uncut;
} // Concat
(2)求子串
Status SubString(SString &Sub, SString S, int pos ,int len ) {
// 用Sub返回串S的第pos个字符起长度为len的子串。
// 其中, 1≤pos≤StrLength(S)且 0≤len≤StrLength(S)- pos+1
if(pos<1||pos>S[0]||len<0||len>S[0]-pos+1)
return ERROR;
Sub[1…len] = S[pos…pos+len-1];
Sub[ 0] = len ; return OK;
} // SubString
操作特点
(1)实现串操作的原操作为“字符序列的复制”
(2)在操作中当出现串的长度超过MAXSTRLEN时,约定用截断法处理。
2、堆分配存储表示
动态分配:这种存储表示的特点是,仍以一组地址连续的存储单元存放串值字符序列,但它们的存储空间是在程序执行过程中动态分配而得。所以也称为动态存储分配的顺序表。在C语言中,利用malloc()和free()等动态存储管理函数,来根据实际需要动态分配和释放字符数组空间。
堆分配的存储表示:
typedef struct {
char *ch;
// 若是非空串,则按串实用长度分配存储区,否则 ch 为NULL
int length; // 串长度
} HString;
基本操作:串插入 Status StrInsert(HString &S,int pos,HString T)
串赋值 Status StrAssign(HString &S,char *chars)
求串长 int StrLength(HString S)
串比较 int StrCompare(HString S,HString T)
串清空 Status ClearString(HString &S)
串联接 Status Concat(HString &S,HString S1,HString S2)
求子串 Status SubString(HString &Sub,HString S,int pos,int len)
1)串赋值
Status StrAssign(HString &S,char *chars) //生成一个值等于chars的串S
{ int i,j; char *c;
for (i=0,c=chars;*c;++i,++c);//求chars的长度
if (!i) {S.ch=NULL; S.length=0;}
else {
if (!(S.ch=(char )malloc(i * sizeof(char))))
exit(OVERFLOW);
for (j=0;j<=i-1;j++){
S.ch[j]=chars[j];}
S.length=i;
}
return OK; }
求串长:
int StrLength(HString S)//求串的长度
{
return S.length;
}
3)串插入
Status StrInsert(HString &S,int pos,HString T)
//在串S的第pos个位置前插入串T
{ int i;
if (pos<1||pos>S.length+1) return ERROR;
if (T.length)
{
if (!(S.ch=(char
) realloc(S.ch,(S.length+T.length)sizeof(char))))
exit(OVERFLOW);
for (i=S.length-1;i>=pos-1;–i)
{
S.ch[i+T.length]=S.ch[i];
}
for (i=0; i<=T.length-1;i++)
S.ch[pos-1+i]=T.ch[i];
S.length+=T.length;
} return OK;
}
4)串比较 int StrCompare(HString S,HString T)//比较两个串,若相等返回0
{
int i;
for (i=0;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;
}
5)串清空
Status ClearString(HString &S)//将S清为空串
{
if (S.ch)
{ free(S.ch); S.ch=NULL;}
S.length=0;
return OK;
}
6)串连接
Status Concat(HString &S,HString S1,HString S2)//用S返回由S1和S2联接而成的新串
{ int j;
if (!(S.ch = (char
)malloc((S1.length+S2.length)
*sizeof(char))))
exit(OVERFLOW);
for (j=0;j<=S1.length-1;j++){ S.ch[j]=S1.ch[j];}
S.length=S1.length+S2.length;
for (j=0;j<=S2.length-1;j++){
S.ch[S1.length+j]=S2.ch[j];}
return OK;
}
7)求子串
Status SubString(HString &Sub,HString S,int pos,int len)//用Sub返回串S的第pos个字符开始长度为len的子串
{
if (pos<1 || pos>S.length || len<0 ||len>S.length-pos+1)
return ERROR;
if (!len) { Sub.ch=NULL; Sub.length=0;}
else {
Sub.ch=(char )malloc(lensizeof(char));
for (int j=0;j<=len-1;j++){
Sub.ch[j]=S.ch[pos-1+j];}
Sub.length=len;
}
return OK;
}
3、串的块链存储表示
为什么要采用链式存储
顺序串上的插入和删除操作不方便,需要移动大量的字符。因此,我们可用单链表方式来存储串值,串的这种链式存储结构简称为链串。
串的链式存储表示:
typedef struct node{
char data;
struct node *next;
}lstring
特点:这种结构便于进行插入和删除运算,但存储空间利用率太低。
串的块链存储表示
为了提高存储密度,可使每个结点存放多个字符。通常将结点数据域存放的字符个数定义为结点的大小,显然,当结点大小大于 1时,串的长度不一定正好是结点的整数倍,因此要用特殊字符来填充最后一个结点,以表示串的终结。
存储密度=串值所占的存储位
实际分配的存储位
串的块链存储表示:
对于结点大小不为1的链串,其类型定为义只需对上述的结点类型做简单的修改即可。
#define nodesize 80
typedef struct node{
char data[nodesize];
struct node *next;
}LString;

4.3串的模式匹配算法

模式匹配:给定主串S="s1s2…sn"和模式T=“t1t2…tm”,在S中寻找T 的过程称为模式匹配。如果匹配成功,返回T在S中的位置,如果匹配失败,返回0。
模式匹配算法:BF(Brute Force)算法,KMP算法
1)BF算法的基本思想:基本思想:从主串S的第一个字符开始和模式T 的第一个字符进行比较,若相等,则继续比较两者的后续字符;否则,从主串S的第二个字符开始和模式T 的第一个字符进行比较,重复上述过程,直到T 中的字符全部比较完毕,则说明本趟匹配成功;或S中字符全部比较完,则说明匹配失败。
2)BF算法的匹配过程
例:主串S=“ababcabcacbab”,模式T=“abcac”
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3)BF算法描述
int Index(SString S,SString T, int pos )
{
i=pos; j=1;
while ( i <= S[0]&&j <= T[0] )
{
if (S[i]==T[j]) { ++i; ++j; }
else { i=i-j+2; j=1; } //i-j为已经比较过的字符个数,+1为当前比较的字符个数+1则到下一个比较字符位置
}
if (j>T[0]) return i-T[0];
else return 0;
}//Index
BF算法时间复杂度分析:设串s长度为n,串t长度为m,在匹配成功的情况下,考虑两种极端情况:
最好情况:不成功的匹配都发生在串t的第一个字符。如在第i趟匹配成功,之前比较的是i-1,加上i趟比较次数m,如第1趟匹配成功是比较m次,第2趟匹配成功是1+m
在这里插入图片描述
因此最好情况下的时间复杂度是O(n+m)。
BF算法时间复杂度分析
设串s长度为n,串t长度为m,在匹配成功的情况下,考虑两种极端情况:
最坏情况:不成功的匹配都发生在串t的最后一个字符。
在这里插入图片描述
一般情况下,m<<n,因此最坏情况下的时间复杂度是O(nm)。
1)KMP算法的基本思想
基本思想:每当一趟匹配过程中出现字符比较不等时,不需要回溯i指针
,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2)KMP算法的匹配过程
i可以不回溯,模式向右滑动到的新比较起点k ,并且k 仅与模式串T有关
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
计算next[j]的方法:
当j=1时,next[j]=0;//next[j]=0表示根本不进行字符比较
当j>1时,next[j]的值为:模式串的位置从1到j-1构成的串中所出现的首尾相同的子串的最大长度加1。
当无首尾相同的子串时next[j]的值为1。next[j]=1表示从模式串头部开始进行字符比较
在这里插入图片描述
在这里插入图片描述
int Index_KMP(SString S,SString T, int pos )
{
i=pos; j=1;
while ( i <= S[0]&&j <= T[0] )
{
if (S[i]==T[j]) { ++i; ++j; }
else j=next[j]; //模式串向右移动
}
if (j>T[0]) return i-T[0];
else return 0;
}//Index
void get_next(SString T, int next[] ) {
// 求模式串T的next函数值并存入数组next。
i = 1; next[1] = 0; j = 0;
while (i < T[0]) {
if (j == 0 || T[i] == T[j])
{++i; ++j; next[i] = j; }
else j = next[j];
}
} // get_next
KMP算法的时间复杂度可以达到O(m+n)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cai-4

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值