第四章 串
4.1串的基本概念
- 字符串是零个或多个字符组成的有限序列。
- 子串:串中任意个连续字符组成的子序列。
- 主串:包含字串的串称为主串。
- 字串在主串中的位置:以字串第一个字符所在的位置表示,即下标+1.
- 串相等:两个串值相等,即长度内容一样。
ADT 串 (String)
Data
串中的元素仅由一个字符组成,相邻元素具有前驱和后继关系.
Operation
StrAssign (&T, chars)
初始条件:chars是字符串常量。
操作结果:生成一个其值等于chars的串T。
StrCopy (&T, S)
初始条件:串S存在。
操作结果:由串S复制得串T。
StrEmpty(S)
初始条件:串S存在。
操作结果:若S为空串,则返回TRUE,否则返回FALSE。
StrCompare(S, T)
初始条件:串S和T存在。
操作结果:若S>T,则返回值>0;若S=T,则返回值=0;若S < T,则返回值 < 0。
StrLength(S)
初始条件:串S存在。
操作结果:返回S的元素个数,称为串的长度。
ClearString (&S)
初始条件:串S存在。
操作结果:将S清为空串。
Concat (&T, S1, S2)
初始条件:串S1和S2存在。
操作结果:用T返回由S1和S2联接而成的新串。
SubString(&Sub, S, pos, len)
初始条件:串S存在,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-pos+1
操作结果:用Sub返回串S的第pos个字符长度为len的子串。
Index(S, T, pos)
初始条件:串S和T存在,T是非空串,1≤pos≤StrLength(S)。
操作结果:若主串S中存在和串T值相同的子串,则返回它在主串S中第pos个字符之后第一次出现的位置;否则函数值为0。
Replace (&S, T, V)
初始条件:串S, T和V存在,T是非空串。
操作结果:用V替换主串S中出现的所有与T相等的不重叠的子串。
StrInsert (&S, pos, T)
初始条件:串S和T存在, 1≤pos≤StrLength(S)+1。
操作结果:在串S的第pos个字符之前插入串T。
StrDelete (&S, pos, len)
初始条件:串S存在, 1≤pos≤StrLength(S)-len+1。
操作结果:从串S中删除第pos个字符起长度为len的子串。
DestroyString (&S)
初始条件:串S存在。
操作结果:串S被销毁。
endADT
4.2串的存储实现
4.2.1定长顺序串
1.定长顺序串储存结构
#define MAXLEN 40
typedef struct{
char ch[MAXLEN];
int len;
}SString;
2.定长顺序串基本操作的实现
(1)串比较函数
void StrCompare(SString s,SString t)
{
int i;
for (i = 0; (i < s.len) && (i<t.len); i++)
if(s.ch[i] != t.ch[i])
return(s.ch[i] - t.ch[i]);
return(s.len - t.len);
}
(2)串删除函数
int 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)串插入函数
int 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);
}
}
(4)定位函数
Brute_Force(暴力查找) O(n*m)
int 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 */
}
Kmp算法
这里有个视频教程,讲的很好。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void prefix_table(char pattern[], int prefix[], int n)
{
prefix[0] = 0;
int len = 0;
int i = 1;
while(i<n)
{
if(pattern[i] == pattern[len]){
len++;
prefix[i] = len;
i++;
}else{
if(len > 0)
len = prefix[len -1];
}else{
prefix[i] = 0;
}
}
}
void move_prefix_table(int prefix[], int n){
int i;
for(i = n-1; i > 0; i--){
prefix[i] = prefix[i - 1];
}
prefix[0] = -1;
}
void kmp_search(char text[], char patern[])
{
int n = strlen(pattern);
int m = strlen(text);
int *prefix = malloc(sizeof(int) *n);
prefix_table(pattern, prefix, n);
move_prefix_table(prefix,n);
//text[i] len(text) = m
//patern[j] len(patern) = n
int i = 0;
int j = 0;
while(i < m){
if (j == n-1 &&text [i] == pattern[j]){
printf("Found pattern at %d\n", i-j);
j = prefix[j];
}
if(text[i] == patern[j]){
i++; j++;
}else{
j = prefix[j];
if(j == -1){
i++;j++;
}
}
}
}
4.2.2堆串
堆串存储方法:以一组地址连续的存储单元存储,空间是动态分配的。
串名符号表:串名,串值之间建立一个对应关系。
堆串存储表示:
typedef struct
{
char* ch;
int len;
}HString;
int 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);
}
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->len=len;
return(1);
}
int 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);
}
4.2.3块链串
由于串是一种线性表,可采用链式存储。一个结点可以放一个或者多个字符。每个结点称为块,整个链表称为快链结构。
#define BLOCK_SIZE 4/*每结点存放字符个数为4*/
typedef struct Block{
char ch [BLOCK_SIZE];
struct Block *next;
} Block;
typedef struct{
Block *head;
Block *tail;
int len;
}BLString;