數據結構與算法系列研究三——字符串
字符串的研究和KMP算法分析和實現
一、串的定義
串是計算機非數值處理的基本對象。串是一種特殊的線性表,它的每個結點僅由一個字符組成,並且單個元素是無意義的。
1、串(string):是由0個或多個字符組成的有限序列,記作:
S=“a1a2...an” (n>=0)
其中:S是串名,兩個雙引號括起來的字符序列為串的值。雙引號不屬於串。
ai(1<=i<=n)為字母、數字或其它符號。
空格符是一個有效字符。
2、串中所有字符的個數n為串的長度。長度為0的串稱為空串。
3、空格串
4、子串:串中任意連續個字符的序列稱為該串的子串。
5、主串:包含該子串的串稱為主串。
6、字符在串中的序號稱為該字符在串中的位置。
7、子串在串中的位置:用子串第一個字符在主串中的位置來表示。空串是任意串的子串。任意串是自身的子串。
8、串常量:不能改變其值的量為常量,串常量一般用直接量表示。
串變量:用於存放字符串值,並且其值可以改變的量。
相關運算:(黃色為基本子集,有這幾種操作可以產生其他復雜操作)
Strassign (&S,chars):將常數串賦值給S,對應於strcopy。
Strlength(S):求串S的長度,對應於strlen(s)。
Strcompare(S1,S2):比較串S1和S2,對應於strcmp(s1,s2)。
Concat(&S,S1,S2):將串S1和S2聯接成一個串,並賦給S。
Substring(&Sub,S,pos,len):在S串中求pos開始的長度為len的子串。
Clearstring(&S):將串S置成空串。
Strcopy (&S,S1):將串S1復制到變量S
Strempty(&S):判斷串S是否為空串。
Index(S,Sub,pos):求子串sub在串S的pos開始後出現的位置。
Replace(&S,Sub,T);用Sub替換S中所有與T相等的不重疊的子串。
Strinsert(&S,pos,Sub):在串S的pos位置前插入子串Sub。
Strdelete(&S,pos,len):在串S中刪除pos位置開始長度為len的子串。
Destroystring(&S):撤消串S。
串的表示與實現—堆分配存儲:
分配一組地址連續的與串長一致的存儲單元存放串值字符序列。與定長順序表示的區別:采用動態分配;串空間與串長一致,串長變化將引起串空間的重新分配。
堆存儲表示:
typedef struct{
char *ch;
int length;
}HString
串的表示與實現—塊鏈表示
塊:一組連續的字符。
塊鏈存儲表示:把串分成指定等長的塊,每一塊用一個結點表示,把各塊鏈成一個鏈表。
當一個結點不滿時,用特殊字符(如‘#’)填充。
若塊的長度為1,就是以單字符為結點的鏈表結構。
塊的大小與存儲密度有關:存儲密度=串值所占存儲位/實際分配存儲位。
單字符結點:插入、刪除方便;存儲密度小,存儲占用量大。
多字符結點:存儲密度大;插入、刪除存在結點的分離,降低存儲密度。
二、字符串的KMP算法
2.1、串的模式匹配算法
串的模式匹配:求子串P在主串T中的位置的定位操作稱為串的模式匹配。
模式串:子串。
2.2、簡單的模式匹配算法:
從主串T的第一個字符起,與模式串P的第一個字符比較,若相等,則繼續逐個比較後繼字符,否則從主串T的第二個字符起再重新和模式串P的第一個字符比較。依次類推,直到模式串P中的每個字符依次和主串T中的一個連續的字符序列相等,則稱匹配成功,返回與P匹配的主串T的字符序列的第一個字符序號。否則稱匹配失敗,函數值為0(或-1)。簡單模式匹配算法存在的問題:在模式匹配過程中存在已比較的字符重復比較。實際上已比較過的字符不必重復比較。
1 int Index(SString T,SString P,int pos) 2 { 3 int i,j,k; 4 int m=strlen(P); 5 int n=strlen(T); 6 for(i=pos-1;i<n-m;i++) 7 { 8 for(j=0,k=i;j<m&&T[k]==P[j]; k++; j++); 9 if(j==m) reurn i; 10 } 11 return -1; 12 }View Code
2.3、KMP算法實現
2.3.1、輸入和輸出
輸入:輸入主串和模式串,以及開始比較的位置
輸出:輸出模式串和主串開始匹配的起始位置,若不匹配則返回0,若匹配則返回匹配的位置。
2.3.2、關鍵數據結構與算法描述
數據結構:字符數組和整形數組
算法描述:使用KMP算法,進行字符串匹配,最大的特點和優點就是i指針不回溯。首先要找到模式串對應的next數組的值。由於找到next數組是為了更好的進行匹配,因此再進行模式串與模式串的匹配求next數組時,可以對next數組進行優化,亦即如果t[i]==t[j],在進行i++,j++後如果t[i]==t[j],則next[i]=next[j];如果不相等,next[i]=j。求出next數組之後,就是KMP算法的主體了,要是j==-1或者s[i]==t[j],i,j都要加一,要不然j=next[j];繼續進行比較,直至子串或主串結束。
2.3.2.1、求next的算法
1 void get_nextval(char *t,int next[]) 2 { 3 int i=0,j=-1; 4 int size=strlen(t); 5 next[0]=-1; 6 while(i<size-1) 7 { 8 if(j==-1||t[i]==t[j]) 9 { 10 i++; 11 j++; 12 13 if(t[i]==t[j]) 14 { 15 next[i]=next[j]; 16 } 17 else 18 next[i]=j; 19 } 20 else 21 j=next[j]; 22 } 23 }View Code
2.3.2.2、KMP算法主體
1 int KMP(char *s,char *p,int position,int next[]) 2 { 3 int i=position-2;//position 為比較位置,從1開始讀 4 int j=-1; 5 int S_SIZE=strlen(s),P_SIZE=strlen(p); 6 while(i<S_SIZE&&j<P_SIZE) 7 { 8 if(j==-1||s[i]==p[j]) 9 { 10 i++; 11 j++; 12 } 13 else 14 j=next[j]; 15 } 16 if(j<P_SIZE) 17 return 0; 18 else 19 return i-j+1; 20 }View Code
2.3.3、測試與理論
1.主串:abcabdsfegabcdsdfg
模式串:abdsfe
起始位置:2,5
理論結果:4,0
2.主串:qqwerttryuriopazgdjcvjkfn
模式串:ttryu
起始位置:3,7
理論結果:6,0
2.3.4、所有代碼
1 #include"stdio.h" 2 #include"string.h" 3 void get_nextval(char *t,int next[]) 4 { 5 int i=0,j=-1; 6 int size=strlen(t); 7 next[0]=-1; 8 while(i<size-1) 9 { 10 if(j==-1||t[i]==t[j]) 11 { 12 i++; 13 j++; 14 15 if(t[i]==t[j]) 16 { 17 next[i]=next[j]; 18 } 19 else 20 next[i]=j; 21 } 22 else 23 j=next[j]; 24 } 25 } 26 27 int KMP(char *s,char *p,int position,int next[]) 28 { 29 int i=position-2;//position 為比較位置,從1開始讀 30 int j=-1; 31 int S_SIZE=strlen(s),P_SIZE=strlen(p); 32 while(i<S_SIZE&&j<P_SIZE) 33 { 34 if(j==-1||s[i]==p[j]) 35 { 36 i++; 37 j++; 38 } 39 else 40 j=next[j]; 41 } 42 if(j<P_SIZE) 43 return 0; 44 else 45 return i-j+1; 46 } 47 48 int main() 49 { 50 char s[100]; 51 char t[100]; 52 int next[100],pos=1; 53 while(1) 54 { 55 56 printf("請輸入主串:\n"); 57 scanf("%s",s); 58 printf("請輸入模式串:\n"); 59 scanf("%s",t); 60 printf("請輸入開始比較的位數(默認為一)\n"); 61 scanf("%d",&pos); 62 63 get_nextval(t,next); 64 printf("開始匹配的起始位置為(若為0則不匹配)\n"); 65 66 printf("%d\n",KMP(s,t,pos,next)); 67 } 68 return 0; 69 }View Code
數據結構與算法系列研究三——字符串