4.2串的顺序存储结构
1、定长顺序串
定长顺序串是将串设计成一种静态结构类型,串的存储分配是在编译时完成的。与前面所讲的线性表的顺序存储结构类似,可用一组地址连续的存储单元存储串的字符序列。
1)定长顺序串存储结构
定长顺序串类型定义如下:
#define MAXLEN 40
typedef struct
{ /*串结构定义*/
char ch[ MAXLEN];
int len;
}SString;
其中 MAXLEN 表示串的最大长度,ch 是存储字符串的一维数组,每个分量存储一 个字符, len 是字符串的长度。
2)定长顺序串基本操作的实现
( 1 )串插入函数
【问题分析】
在进行顺序串的插入时,插入位置 pos 将串分为两部分(假设为 A、 B,长度为 LA、 LB)及待插入部分(假设为 C,长度为 LC),则串由插入前的 AB 变为 ACB,由于是顺序串, 插入会引起元素的移动。
可能出现以下三种情况:
- ①插入后串长( LA+LC +LB)≤ MAXLEN,则将 B 后移 LC 个元素位置,再将 C 插入。
- ②插入后串长>MAXLEN 且 pos+LC≤ MAXLEN,则 B 后移时会有部分字符被舍弃。
- ③插入后串长> MAXLEN 且 pos +LC >MAXLEN,则 B 的全部字符被舍弃(不需后移),并 且 C 在插入时也有部分字符被舍弃。
【算法描述】
StrInsert(SString *s, int pos, SString t)
/*在串 s 中下标为 pos 的字符之前插入串 t */
{
int i;
if (pos<0 || pos>s->len)
return(0); /*插入位置不合法*/
if (s->len + t.len<=MAXLEN)
{ /*插入后串长≤MAXLEN*/
for (i=s->len + t.len-1;i>=t.len + pos;i--)
s->ch[i]=s->ch[i-t.len];
for (i=0;i<t.len;i++)
s->ch[i+pos]=t.ch[i];
s->len=s->len+t.len;
}
else if (pos+t.len<=MAXLEN)
{/*插入后串长>MAXLEN,但串 t 的字符序列可以全部插入*/
for (i=MAXLEN-1;i>t.len+pos-1;i--)
s->ch[i]=s->ch[i-t.len];
for (i=0;i<t.len;i++)
s->ch[i+pos]=t.ch[i];
s->len=MAXLEN;
}
else
{ /*插入后串长>MAXLEN,并且串 t 的部分字符也要舍弃*/
for (i=0;i<MAXLEN-pos;i++)
s->ch[i+pos]=t.ch[i];
s->len=MAXLEN;
}
return(1);
}
显然,实现顺序串插入的算法其实现复杂度为: O( s->len+t.len)。
( 2 )串删除函数
StrDelete(SString *s, int pos, int len)
/*在串 s 中删除从下标 pos 起 len 个字符*/
{
int i;
if (pos<0 || pos>(s->len-len))
return(0); /*删除参数不合法*/
for (i=pos+len;i<s->len;i++)
s->ch[i-len]=s->ch[i];
/*从 pos+len 开始至串尾依次向前移动,实现删除 len 个字符*/
s->len=s->len - len;
/*s 串长减 len*/
return(1);
}
(3)串的简单模式匹配 Brute-Force(布鲁特-福斯)算法
【算法思想】
简单的模式匹配算法是一种带回溯的匹配算法,算法的基本思想是:从主串 S 的第 pos 个字符开始,和模式串 T 的第一个字符开始比较,如果相等,就继续比较后续字符, 如果不等,则从(回溯到)主串 S 的第 pos+1 个字符开始重新和模式串 T 比较,直到模式串 T 中 的每一个字符和主串 S中的一个连续字符子序列全部相等,则称匹配成功,返回和 T 中第一个 字符相等的字符在主串 S 中的位置;或者主串中没有和模式串相等的字符序列,则称匹配不成功。
【算法描述】
实现时设 i、 j、 start 三个指示器:
i——指向主串 S 中当前比较的字符,起始指向 S 的首字符,此后,每比较一步,后移一步,一 趟匹配失败时,回溯到该趟比较起点的下一位置。
j———指向子串T中当前比较的字符,起始指向T的首字符,此后,每比较一步,后移一步, 一趟匹配失败时,回溯到 T 的首字符处。
start———记录每趟比较时在主串 S 中的起点,每趟比较后,后移一步,以便确定下一趟的起始位置。
顺序串的简单模式匹配(定位)函数
StrIndex(SString s,int pos, SString t)
/*求从主串 s 的下标 pos 起,串 t 第一次出现的位置,成功返回位置序号,不成功返回-1*/
{
int i, j, start;
if (t.len==0)
return(0); /* 模式串为空串时,是任意串的匹配串 */
start=pos;
i=start;
j=0; /* 主串从 pos 开始,模式串从头(0)开始 */
while (i<s.len && j<t.len)
if (s.ch[i]==t.ch[j])
{
i++;
j++;
} /* 当前对应字符相等时推进 */
else
{
start++; /* 当前对应字符不等时回溯 */
i=start;
j=0; /* 主串从 start+1 开始,模式串从头(0)开始*/
}
if (j>=t.len)
return(start); /* 匹配成功时,返回匹配起始位置 */
else
return(-1); /* 匹配不成功时,返回-1 */
}
【算法分析】
该算法思路比较简单,但最坏时间复杂度较高,为 O( s.len* t.len),如主串为 51 个 0 ,模式串为 7 个 0 后有个 1 ,每趟都在最后一个不匹配后而倒到 start+1 ,整个匹配过程 共需 s.len- t.len=45 趟,每趟比较 t.len=8 个字符,故此例整个比较了 360 次。这个算法的主要时间耗费在失配后的比较位置有回溯,造成了比较次数太多的情况。降低时间复杂度可采用无回溯的算法。
2、堆串
字符串包括串名与串值两部分,而串值采用堆串存储方法存储,串名用符号表 存储。
堆串存储方法:仍以一组地址连续的存储单元顺序存放串中的字符,但它们的 存储空间是在程序执行过程中是动态分配的。系统将一个地址连续、容量很大的 存储空间作为字符串的可用空间,每当建立一个新串时,系统就从这个空间中分配 一个大小和字符串长度相同的空间存储新串的串值。
串名符号表:所有串名的存储映像构成一个符号表。借助此结构可以在串名和串值之间建立一个对应关系,称为串名的存储映像。
1)堆串存储表示:
C 语言已经有一个称为“堆”的自由存储空间,并可用函数 malloc()和函数 free()完成动态存储管理。
因此,可以直接利用 C 语言中的“堆” 来实现堆串。此时堆串可定义如下:
typedef struct
{
char * ch;
int len;
}HString;
其中 len 域指示串的长度,ch 域指示串的起始地址。
2)堆串基本操作的实现
( 1 )堆串插入函数
StrInsert(HString *s,int pos,HString *t)
/*在串 s 中下标为 pos 的字符之前插入串 t */
{
int i;
char *temp;
if (pos<0 || pos>s->len || s->len==0)
return(0); /*插入位置不合法*/
temp=(char *)malloc(s->len + t->len); /*动态产生足够的空间存放插入后的串*/
if (temp==NULL)
return(0);
for (i=0;i<pos;i++)
temp[i]=s->ch[i];
for (i=0;i<t->len;i++)
temp[i+pos]=t->ch[i];
for(i=pos;i<s->len;i++)
temp[i + t->len]=s->ch[i];
s->len+=t->len;
free(s->ch);
s->ch=temp;
return(1);
}
( 2 )堆串删除函数
StrDelete(HString *s,int pos,int len)
/*在串 s 中删除从下标 pos 起 len 个字符 */
{
int i;
char *temp;
if (pos<0 || pos>(s->len - len))
return(0); /*删除参数不合法*/
temp=(char *)malloc(s->len - len);
if (temp==NULL) return(0);
for (i=0;i<pos;i++)
temp[i]=s->ch[i];
for (i=pos;i<s->len - len;i++)
temp[i]=s->ch[i+len];
s->len=s->len-len;
free(s->ch);
s->ch=temp;
return(1);
}