一、 串類型的定義
- 1. 串的定義
串(string)(或字符串)是由零個或多個字符組成的有序序列,一般記為
S=”a1a2…an” (n>=0)
其中,s是串的名,用雙引號括起來的字符序列是串的值;ai (1≤i≤n)可以是字母、數字或其他字符;串中字符的數目n成為串的長度。零個字符的串稱為空串(null string),它的長度為0。
串中任意個連續的字符組成的子序列稱為該串的子串。包含子串的串相應的稱為主串。通常稱字符在序列中的序號為該字符在串中的位置。子串在主串中的位置則以子串的第一個字符在主串中的位置來表示。
例如,假設有a、b、c、d四個字符串:
a=”BEI” b=”JING”
c=”BEIJING” d=”BEI JING”
則它們的長度分別為3、4、7、8;並且a、b都是c、d的子串,a在c和d中的位置都是0,而b在c中的位置是3,在d中的位置是4;可見字符串的索引是從0開始的。
稱兩個串相等的,當且僅當這兩個串的值相等。也就是說,只有當兩個字符串的長度相等,並且各個對應位置的字符都相等才相等。例如上例子中a、b、c、d都互不相等。
值得一提的是,串值必須用一對雙引號括起來,但雙引號本身不屬於串,它的作用只是為了避免與變量名或數的常量混淆而已。另在高級語言中,如C語言中字符串中隱藏一個特殊的字符即’\0’,它的作用是標識字符串結束,因此稱’\0’為字符串結束符。
- 2. 串的操作
字符串可以有豐富的操作,在日常非數值運算大量的都是字符串的操作。歸納總結可以大致分為以下操作
(1)賦值 strcpy(數組名、字符串);
(2)判空 strlen() strcmp()
(3)字符串比較 strcmp
(4)求字符串長度 strlen
(5)字符串拷貝 strcpy
(6)字符串連接 strcat
(7)求字符串子串(截取字符串)
二、 串類型的表示和實現
- 1. 串的表示
(1)定長順序存儲表示:類似線性表的順序存儲結構,用一組地址連續的存儲單元存儲字符串的字符序列。
(2)堆分配存儲表示:這種存儲方式特點是:仍一一組連續的存儲單元存放字符串序列,但它們的存儲空間是在程序執行過程中動態分配而得的。
(3)塊鏈存儲表示:和線性表的鏈式存儲類似,也是用鏈表來保存字符串的值。根據串的特殊,若每個字符占一個結點太小,則采取每個結點可以存放一個也可以存放多個字符。如圖所示:圖a就是塊鏈存儲方式,而圖b則過於浪費空間。
- 1. 串的實現
首先串的存儲方式不同其實現方式就是不一樣的,這里我們采用第一種存儲方式即定長順序存儲表示。
#define MAX 255 //用戶可在255以內定義最大串長
typedef unsigned char SString[MAX+1]; //最后以’\0’結束標識字符串結束
當如此頂以后串的各種操作就可以實現了,有一些C語言中自帶的系統可完成一部分,如字符串拷貝strcpy,字符串連接strcat,字符串比較strcmp,求字符串長度strlen。也有另外一部分需要自己實現,比如截取字符串substring:
char * substring(char sub[],char s[],int pos,int len) { int i,count; if(pos<0 ||pos>strlen(s)-1||len<0||len>strlen(s)-pos) //判斷pos(開始截取位置)是否合法 { //判斷len(截取長度)是否合法 printf("輸入參數有誤"); return "error"; } else { count=0; for(i=pos;i<pos+len;i++) //截取字符串 { sub[count]=s[i]; count++; } return sub; } } void main() { char s[100]="I love BAWEI University"; char sub[20]; printf("%s",substring(sub,s,2,4)); }
運行效果如圖所示:
一、 串的模式匹配算法
子串的定位操作通常稱為串的模式匹配,是各種串處理系統中最重要的操作之一。
- 1. ACM算法:子串的定位函數Index(S,T,pos)
int index(char s[],char t[],int pos)//返回子串t在主串s中的位置,pos指定的是在主串的第pos開始出現子串的位置,若全串查找pos設置為0 { int i,j; i=pos; j=0; while(i<strlen(s)&&j<strlen(t)) { if(s[i]==t[j]) { i++; j++; //繼續比較后面的字符 } else { i=i-j+1; //i后退重新開始匹配 j=0; } } if(j==strlen(t)) { return i-strlen(t); } else { return -1; //沒有匹配到 } } void main() { char s[100]="ababcabcacbaab"; char t[100]="abcac"; int pos=0; printf("%d",index(s,t,pos)); }
運行結果是5,即在主串下標5的位置出現abcac這個子串。下面我們看下這個匹配的過程示意圖:
↓i=2
第一趟匹配 a b a b c a b c a c b a b
a b c
↑j=2
↓i=1
第二趟匹配:a b a b c a b c a c b a b
a
↑j=0
↓i=6
第三趟匹配:ab a b c a b c a c b a b
a b c a c
↑j=4
↓i=3
第四趟匹配:a b a b c a b c a c b a b
a
↑j=0
↓i=4
第五趟匹配:a b a b c a b c a c b a b
a
↑j=0
↓i=10
第六趟匹配:a b a b c a b c a c b a b
a b c a c
↑j=5
- 2. ACM算法:模式匹配的一種改進算法(KMP算法)
這種改進算法是D.E.Knuth與V.R.Pratt和J.H.Morris同時發現的,因此人們稱它為克努特-莫里斯-普拉特操作(簡稱KMP算法)。此算法可以在O(n+m)的時間數量級上完成串的模式匹配操作。其改進在於:每趟匹配過程出現字符比較不等時,不需要回溯i指針,而是利用已經得到的“部分匹配”的結果將模式串向右“滑動”盡可能遠的一段距離后,繼續進行比較。下面先從具體例子看起。
回顧上小節的匹配過程示例,在第三趟的匹配過程中,當i=6,j=4字符比較不等時,又從i=3,j=0重新開始比較。然后,經仔細觀察可發現,在i=3和j=0,i=4和j=0,及i=5和j=0這三次比較都是不必進行的。因為從第三趟部分匹配的結果就可得出,主串中下標3、4、5的字符必然是‘b’、‘c’、‘a’(即模式串下標為1、2、3的字符)。因為模式中第一個字符是a。因此它無需再和這三個字符進行比較,而僅需將模式向右滑動3個字符的位置繼續進行i=6,j=1時的字符比較即可。同理,在第一趟匹配中出現字符不等時,僅需將模式向右移動兩個字符的位置繼續進行i=2,j=0時的字符比較。由此,在整個匹配過程中,i指針沒有回溯,如下圖所示。
↓i=2
第一趟匹配 a b a b c a b c a c b a b
a b c
↑j=2
↓i → ↓i=6
第二趟匹配 a b a b c a b c a c b a b
a b c a c
↑ → ↑j=4
↓i → ↓i=10
第三趟匹配 a b a b c a b c a c b a b
(a ) b c a c
↑j=2→↑j=5
現在討論一般情況。假設主串為“s1s2。。。sn”,模式串為“p1p2。。。pm”,從上例的分析可知,為了實現改進算法,需要解決下述問題:當匹配過程中產生“失配”即(si ≠pj)時,模式串“向右滑動”可行的距離多遠,換句話說,當主串下標i的字符與模式中下標j的字符失配(即比較不等)時,主串下標i的字符(i指針不回溯)應與模式中哪個字符再比較。
假設此時應與模式中下標k(k<j)的字符進行比較,則模式中前k-1字符的子串必須滿足下列關系式,且不可能存在k’>k滿足下列關系式
“p1p2。。。Pk-1”=“si-k+1si-k+2。。。si-1” ①
而已經得到的“部分匹配”的結果是
“pj-k+1pj-k+2。。。Pj-1”=“si-k+1si-k+2。。。si-1” ②
由式子①和式子②可知
“p1p2。。。Pk-1”=“pj-k+1pj-k+2。。。Pj-1” ③
反之,若模式串存在滿足式③的兩個子串,則當匹配過程中,主串中第i個字符與模式中第j個字符比較不等時,僅需要將模式串向右滑動值模式中第k個字符和主串第i個字符對齊,此時,模式中頭k-1個字符的子串“p1p2。。。Pk-1”必定與主串中第i個字符之前長度k-1的子串“si-k+1si-k+2。。。si-1”相等,因此,匹配僅需從模式中第k個字符與主串的第i個字符比較起繼續進行。
設next[j]=k,則next[j]表明當第j個字符與主串中相應字符失配時,在模式串需中心和主串中字符進行比較的字符位置。由此可引出模式串的next函數的定義
由此定義可推出下列模式串的next函數值:
算法如下:
int get_next(char t[],int next[]) //計算next[j]的函數 { int i=0,j=0; next[0]=0; while(i<strlen(t)) { if(j==0||t[i]==t[j]) { i++; j++; next[i]=j; } else { j=next[j]; } } } int index(char s[],char t[],int pos) //子串定位函數 { int i,j; int next[256]; get_next(t,next); for(j=0;j<strlen(t);j++) { printf("%d\t",next[j]); } i=pos; j=0; while(i<strlen(s)&&j<strlen(t)) { if(j==-1||s[i]==t[j]) { i++; j++; } else { j=next[j]-1; //失配時只滑動模式串到next[j]-1的位置繼續比較即可 } } if(j>=strlen(t)) { return i-strlen(t); } else { return -1; //沒有匹配到 } } void main() { char s[100]="ababcabcacbaab"; char t[100]="abcac"; int pos=0; printf("%d",index(s,t,pos)); //打印子串在主串的位置 }
一、 串的算法時間復雜度的分析
一般的來講,對於字符串模式匹配算法時間復雜度是與主串的長度n和子串的長度m相關的,對於未改進的定位子串的算法:最好情況時間復雜度為O(n+m)而最壞情況則可達到O(n*m),因為其i指針是要回溯的。而對於改進的模式匹配算法KMP算法其i指針不用回溯,只移動模式串的比較位置即可。這樣時間復雜度就在O(n+m)級別可完成算法。
生日快樂代碼
#include "stdio.h" #include "stdlib.h" #include "windows.h" #include "mmsystem.h" #include "time.h" #pragma comment(lib,"Winmm.lib") int i,j; void gotoxy(int x,int y){ COORD c; c.X=x; c.Y=y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c); } int color(int c){ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),c); return 0; } void xin(){ color(12); gotoxy(18,8); printf("■");//8行28列 Sleep(500); gotoxy(16,7); printf("■"); Sleep(500); gotoxy(14,6); printf("■"); Sleep(500); gotoxy(12,6); printf("■"); Sleep(500); gotoxy(10,7); printf("■"); Sleep(500); gotoxy(10,8); printf("■"); Sleep(500); gotoxy(10,9); printf("■"); Sleep(500); gotoxy(12,10); printf("■"); Sleep(500); gotoxy(14,11); printf("■"); Sleep(500); gotoxy(16,12); printf("■"); Sleep(500); gotoxy(18,13); printf("■"); Sleep(500); gotoxy(20,12); printf("■"); Sleep(500); gotoxy(22,11); printf("■"); Sleep(500); gotoxy(24,10); printf("■"); Sleep(500); gotoxy(26,9); printf("■"); Sleep(500); gotoxy(26,8); printf("■"); Sleep(500); gotoxy(26,7); printf("■"); Sleep(500); gotoxy(24,6); printf("■"); Sleep(500); gotoxy(22,6); printf("■"); Sleep(500); gotoxy(20,7); printf("■"); for(i=7;i<10;i++){ gotoxy(12,i); printf("■"); } for(i=7;i<11;i++){ gotoxy(14,i); printf("■"); } for(i=8;i<12;i++){ gotoxy(16,i); printf("■"); } for(i=9;i<13;i++){ gotoxy(18,i); printf("■"); } for(i=7;i<10;i++){ gotoxy(24,i); printf("■"); } for(i=7;i<11;i++){ gotoxy(22,i); printf("■"); } for(i=8;i<12;i++){ gotoxy(20,i); printf("■"); } } void xinr(){ color(4); for(i=10;i<50;i+=2){ gotoxy(i,15); if(i%2==0){ printf("◢");} else{ printf("◣"); } Sleep(100); } color(12); for(i=8;i<52;i+=2){ gotoxy(i,16); printf("■"); Sleep(100); } color(4);//11淺藍色 for(i=6;i<54;i+=2){ gotoxy(i,17); printf("■"); Sleep(100); } color(15); for(i=6;i<54;i+=2){ gotoxy(i,18); printf("■"); Sleep(100); } color(5);//紫色 for(i=6;i<54;i+=2){ gotoxy(i,19); printf("■"); Sleep(100); } color(12); for(i=4;i<56;i+=2){ gotoxy(i,20); printf("■"); Sleep(100); } //蠟燭 color(6); gotoxy(14,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(14,i); printf("■"); Sleep(100); } for(i=15;i>12;i--){ gotoxy(16,i); printf("■"); Sleep(100); } color(6); gotoxy(16,11); printf("▲"); Sleep(100); color(6); gotoxy(26,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(20,i); printf("■"); Sleep(100); } for(i=15;i>11;i--){ gotoxy(26,i); printf("■"); Sleep(100); } color(6); gotoxy(20,11); printf("▲"); Sleep(100); color(6); gotoxy(32,11); printf("▲"); Sleep(100); color(12); for(i=15;i>11;i--){ gotoxy(32,i); printf("■"); Sleep(100); } color(6); gotoxy(44,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(44,i); printf("■"); Sleep(100); } color(6); gotoxy(38,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(38,i); printf("■"); Sleep(100); } for(i=15;i>12;i--){ gotoxy(46,i); printf("■"); Sleep(100); } color(6); gotoxy(46,11); printf("▲"); Sleep(100); } void menu(){ for(i=0;i<58;i+=2){ gotoxy(i,1); color(7);//4紅色5紫色7白色9藍色10lvse printf("□"); gotoxy(i,26); printf("□"); } for(i=0;i<26;i++){ gotoxy(0,i); printf("□"); gotoxy(56,i); printf("□"); } for(i=2;i<56;i+=2){ for(j=2;j<26;j++){ gotoxy(i,j); color(0); printf("■"); } } } //happy birthday void birthday(){ int i; color(6); //56 20 H for(i=22;i<25;i++){ gotoxy(10,i); printf("■"); } gotoxy(12,23); printf("■"); gotoxy(14,22); printf("◣"); for(i=23;i<25;i++){ gotoxy(14,i); printf("■"); } Sleep(400); //a gotoxy(18,24); printf("■"); gotoxy(18,23); printf("◢"); gotoxy(20,22); printf("▲"); gotoxy(20,23); printf("▁"); gotoxy(22,24); printf("■"); gotoxy(22,23); printf("◣"); //p左邊是列 Sleep(400); color(2); gotoxy(26,22); printf("◤"); gotoxy(26,23); printf("◣"); gotoxy(26,24); printf("■"); gotoxy(28,22); printf("◥"); gotoxy(28,23); printf("◢"); //p Sleep(400); color(5); gotoxy(32,22); printf("◤"); gotoxy(32,23); printf("◣"); gotoxy(32,24); printf("■"); gotoxy(34,22); printf("◥"); gotoxy(34,23); printf("◢"); //y Sleep(400); color(4); gotoxy(38,22); printf("◢"); gotoxy(38,23); printf("■"); gotoxy(40,25); printf("■"); gotoxy(40,23); printf("■"); for(i=22;i<25;i++){ gotoxy(42,i); printf("■"); } gotoxy(42,25); printf("◤"); Sleep(400); //b color(10); gotoxy(2,3); printf("◢"); for(i=4;i<7;i++){ gotoxy(2,i); printf("■"); } gotoxy(4,6); printf("◢"); gotoxy(4,5); printf("◥"); //i Sleep(400); color(4); gotoxy(8,3); printf("■"); gotoxy(8,5); printf("■"); gotoxy(8,6); printf("■"); //r Sleep(400); color(12); for(i=4;i<7;i++){ gotoxy(12,i); printf("■"); } //gotoxy(14,5); //printf("■"); gotoxy(14,4); printf("◤"); //t Sleep(400); color(6); gotoxy(18,5); printf("■"); gotoxy(20,5); printf("■"); gotoxy(22,5); printf("■"); gotoxy(20,4); printf("■"); gotoxy(20,6); printf("■"); gotoxy(20,7); printf("■"); gotoxy(22,7); printf("◣"); Sleep(400); //H color(6); for(i=4;i<7;i++){ gotoxy(26,i); printf("■"); } gotoxy(28,5); printf("■"); gotoxy(30,5); printf("◣"); gotoxy(30,6); printf("■"); Sleep(400); //d color(10); for(i=3;i<7;i++){ gotoxy(36,i); printf("■"); } gotoxy(34,6); printf("■"); gotoxy(34,5); printf("◤"); //a Sleep(400); color(12); gotoxy(42,4); printf("■"); gotoxy(40,6); printf("◥"); gotoxy(40,4); printf("◢"); gotoxy(40,5); printf("■"); gotoxy(42,5); printf("■"); gotoxy(42,6); printf("■"); gotoxy(44,6); printf("◤"); //y Sleep(400); color(12); gotoxy(48,3); printf("◢"); gotoxy(48,4); printf("■"); gotoxy(50,4); printf("■"); gotoxy(52,4); printf("■"); gotoxy(52,5); printf("■"); gotoxy(52,6); printf("◤"); gotoxy(52,3); printf("■"); gotoxy(50,6); printf("■"); } void main() { PlaySound(TEXT("sounds\\taiy.wav"),NULL,SND_FILENAME|SND_ASYNC|SND_LOOP); system ( "mode con cols=60 lines=28" ); system("title "); menu(); gotoxy(28,0); system("pause"); xinr(); birthday(); gotoxy(10,0); system("pause"); system("cls"); xin(); }