第四章 串
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;