字符串是最基本的非数值数据,字符串是一种特定的线性表,其特殊性在于组成线性表的每个元素就是一个单字符。
串的存储结构
(1)定长顺序串:定长顺序存储表示:用一组地址连续的存储单元存储串值的字符序列
#define MaxSize 255//预定义最大串长为255
typedef struct {
char ch[MaxSize];//每个分量存储一个字符
int length;//串的实际长度
}SString;
//在串s中下标为pos的字符之前插入串t
int StrInsert(SString* s, int pos, SString t) {
int i;
if (pos<0 || pos>s->length)//插入位置不合法
return (0);
if (s->length + t.length <= MaxSize) {//插入后串长<=MaxSize
for (i = s->length + t.length - 1; i >= t.length + pos; i--)
s->ch[i] = s->ch[i - t.length];
for (i = 0; i < t.length; i++)
s->ch[i + pos] = t.ch[i];
s->length = s->length + t.length;
}
else if (pos + t.length <= MaxSize) {//插入后串长>MaxSize,但串t的字符序列可以全部插入
for (i = MaxSize - 1; i > t.length + pos - 1; i--)
s->ch[i] = s->ch[i-t.length];
for (i = 0; i < t.length; i++)
s->ch[i + pos] = t.ch[i];
s->length = MaxSize;
}
else {//插入后串长>MaxSize,但串t的部分字符也要舍弃
for (i = 0; i < MaxSize - pos; i++)
s->ch[i + pos] = t.ch[i];
s->length = MaxSize;
}
return (1);
}
//顺序串删除函数,在串s中删除从下标pos起len个字符
int StrDelete(SString* s, int pos, int len) {
int i;
if (pos<0 || pos>(s->length - len))//删除参数不合法
return (0);
for (i = pos + len; i < s->length; i++)
s->ch[i - len] = s->ch[i];//从pos+len开始至串依次向前移动,实现删除len个字符
s->length = s->length - len;//s串长减len
return (1);
}
//串比较函数,若串s和t相等返回0,若s>t则返回正数,若s<t则返回负数
int StrCompare(SString s, SString t) {
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);
}
//串的实际长度只能小于等于MaxSize,超过预定义长度的串值会被舍去,称为截断
(2)堆串:堆分配存储仍然是以一组地址连续的存储单元存放串值的字符序列,但它们的存储空间是在程序执行过程中动态分配得到的。
typedef struct {
char* ch;//按串长分配存储区,ch指向串的基地址
int length;//串的长度
}HString;
//在串s下标为pos的字符之前插入串t
int StrInsertd(HString* s, int pos, HString* t) {
int i;
char* temp;
if (pos < 0 || pos>s->length || s->length == 0)//插入位置不合法
return (0);
temp = (char*)malloc(s->length + t->length);//动态产生足够的空间存放插入后的串
if (temp == NULL)
return (0);
for (i = 0; i > pos; i++)
temp[i] = s->ch[i];
for (i = 0; i < t->length; i++)
temp[i + pos] = t->ch[i];
for (i = pos; i < s->length; i++)
temp[i + t->length] = s->ch[i];
s->length += t->length;
free(s->ch);
s->ch = temp;
return(1);
}
//堆串赋值函数
int StrAssign(HString* s, char* tval) {
//将字符串常量tval的值赋给堆串s
int len, i = 0;
if (s->ch != NULL)
free(s->ch);
while (tval[i] != '\0')
i++;
len = i;
if (len) {
s->ch = (char*)malloc(len);
if (s->ch == NULL)
return (0);
for (i = 0; i < len; i++)
s->ch[i] = tval[i];
}
else
s->ch = NULL;
s->length = len;
return(1);
}
//堆串删除字符串:删除字符串S中从pos位置开始的len长度子串
void StrDelete(HString* S, int pos, int len)
{
//判断是否不满足删除条件
if (pos<0 || pos>S->length)
return;
if (len <= 0 || len > S->length)
return;
int j = pos; //设置开始删除的位置
//将pos+len处及其之后的字符全部往前移动len长度,通过覆盖实现删除
for (int i = j + len; i < S->length; ++i)
{
S->ch[i - len] = S->ch[i];
}
//修改删除后S的长度
S->length -= len;
}
(3)块链串
typedef struct StringNode {
char ch;//每个结点存一个字符
struct StringNode* next;
}StringNode,*String;
typedef struct Stringnode {
char ch[4];//每个结点存四个字符
struct Stringnode* next;
}Sstringnode,*Sstring;
简单模式匹配算法思想:从主串S的第pos个字符开始,和模式串T的第一个字符开始比较,如果相等,就继续比较后续字符,如果不等,则从主串S的第pos+1个字符开始重新和模式串T比较,直到模式串T中的每个字符和主串S中的一个连续字符子序列全部相等,则称匹配成功,返回和T中第一个字符相等的字符在主串S中的位置;或者主串中没有和模式串相等的字符序列,则称匹配不成功。
实现时设i,j,start三个指示器:
i指向主串S中当前比较的字符,起始指向S的首字符,此后,每比较一步,后移一个位置,一趟匹配失败时,回溯到该趟比较起点的下一个位置。
j指向子串T中当前比较的字符,起始指向T的首字符,此后,每比较一步,后移一个位置,一趟匹配失败时,回溯到T的首字符处。
start记录每趟比较时在主串S中的起点,每趟比较后,后移一个位置,以便确定下一趟的起始位置。
//求主串S的下标pos起,串t第一次出现的位置,成功返回位置序号,不成功返回-1
int StrIndex(SString S,int pos, SString T) {
int i, j,start;
if (T.length == 0)//当模式串为空串时,是任意串的匹配串
return (0);
start = pos; i = start; j = 0;//主串从pos开始,模式串从头(0)开始
while (i < S.length && j < T.length) {
if (S.ch[i] == T.ch[j]) {//当前对应字符相等时推进
++i;
++j;
}
else {//当前对应字符不等时回溯
start++;
i = start;//主串从start+1开始,模式串从头(0)开始
j = 0;
}
}
if (j >=T.length)
return (start);//匹配成功时,返回匹配起始位置
else
return -1;//匹配不成功时,返回-1
}
主串长度为n,模式串长度为m:
简单模式匹配最好的情况是每个子串的第一个字符就匹配失败O(n-m+1)=O(n)
简单模式匹配最坏的情况是每个子串都要对比m个字符,共n-m+1个子串,时间复杂度:O((n-m+1)*m)=O(nm)