題目描述:妞妞有兩個字符串a和b,其中a串是一個01串,b串中除了可能有0和1,還可能有'?',b中的'?'可以確定為0或者1。尋找一個字符串t是否在字符串s中出現的過程,稱為字符串匹配。牛牛現在考慮所有可能的字符串b,有多少種可以在字符串a中完成匹配。
例如:a="00010001",b="??",字符串b可能的字符串是"00","01","10","11",只有"11"沒有出現在字符串a中,所以輸出3。
輸入描述:輸入包括兩行,第一行是字符串a,長度在1-50,第二行是字符串b,長度是1-50,輸出為一個數,表示匹配數量。
解題:
1)Main函數編寫
1 int main(int argc, const char* argv[]){ 2 char * str = malloc(sizeof(char)*MAXLEN); 3 char * pattern = malloc(sizeof(char)*MAXLEN); 4 scanf("%s",str); 5 scanf("%s",pattern); 6 int strlen = 0; 7 while (str[strlen]!='\0') { 8 ++strlen; 9 } 10 int patternlen = 0; 11 while (pattern[patternlen]!='\0') { 12 ++patternlen; 13 } 14 char **p1 = malloc(sizeof(char*)*MAXLEN); 15 char **p2 = malloc(sizeof(char*)*MAXLEN); 16 for (int i = 0; i <= strlen - patternlen; ++i) { 17 p1[i] = str+i; 18 } 19 //sort(p1, 0,strlen-patternlen); 20 qsort(p1, strlen-patternlen+1, sizeof(char*), cmp); 21 int i = 0; 22 int j = 1; 23 p2[0] = p1[0]; 24 while (j <= strlen - patternlen) { 25 if (cmp2(p2[i],p1[j], patternlen) == 0) { 26 ++j; 27 continue; 28 } 29 //p2[++i] = p1[j++]; 30 ++i; 31 p2[i] = p1[j]; 32 ++j; 33 } 34 strlen = i+1; 35 j = 0; 36 int ans = 0; 37 while(j<strlen){ 38 if (match(p2[j], pattern, patternlen) == 0) { 39 ++ans; 40 } 41 ++j; 42 } 43 printf("%d\n",ans); 44 return 0; 45 }
2)自實現的后綴數組比較函數cmp(該cmp的傳入參數為const char*類型,方便作為標准庫函數qsort的參數傳入)
1 int cmp(const void * x, const void * y){ 2 int i = 0; 3 char * a = *(char**)x; 4 char * b = *(char**)y; 5 while (a[i]!='\0'&&b[i]!='\0') { 6 if (a[i]<b[i]) { 7 return -1; 8 } else if(a[i] > b[i]){ 9 return 1; 10 } else { 11 ++i; 12 } 13 } 14 if (a[i]=='\0'&&b[i]=='\0') { 15 return 0; 16 } else if(a[i]=='\0'){ 17 return -1; 18 } else { 19 return 1; 20 } 21 }
3)因為c語言無法給函數形參配置默認值,故需重新實現一個帶有長度限制的字符串比較函數
//或者使用int strncmp(const char * s1, const char * s2, size_t n)
1 int cmp2(char * a, char * b, int len){ 2 int i = 0; 3 while (a[i]!='\0'&&b[i]!='\0'&& i<len) { 4 if (a[i]<b[i]) { 5 return -1; 6 } else if(a[i] > b[i]){ 7 return 1; 8 } else { 9 ++i; 10 } 11 } 12 if (i==len) { 13 return 0; 14 } 15 if (a[i]=='\0'&&b[i]=='\0') { 16 return 0; 17 } else if(a[i]=='\0'){ 18 return -1; 19 } else { 20 return 1; 21 } 22 }
4)自實現的快速排序算法
1 void sort(char** arr, int start, int end){ 2 if (end-start <= 0) { 3 return; 4 } 5 char* pivot = arr[start]; 6 int i = start+1; 7 int j = end; 8 while (i<=j) { 9 while (cmp(arr[i],pivot)<1) { 10 ++i; 11 } 12 while(cmp(arr[j],pivot)==1){ 13 --j; 14 } 15 if (i>=j) { 16 break; 17 } 18 char * temp = arr[i]; 19 arr[i] = arr[j]; 20 arr[j] = temp; 21 } 22 23 arr[start] = arr[j]; 24 arr[j] = pivot; 25 sort(arr, start, j-1); 26 sort(arr, j+1, end); 27 }
總結:
1)c語言標准庫共包含15個頭文件
Header File | Content |
stdio.h | 輸入和輸出, 包括scanf,printf,putchar,getchar,puts,gets,fopen,fclose,fread,fwrite等 |
stdlib.h | 最常用的系統工具函數,包括 1)#define NULL (void*)0 2)內存管理函數 void * malloc(size_t size), void free(void * ptr) 3)數學函數 abs, labs, rand, srand 4)字符串轉換函數 atoi, atol, atof 5)搜索和排序函數 void qsort(void * base, size_t nmemb, size_t size, int (* compar)(const void *, const void *))、void * bsearch(const void * key, const void * base, size_t nmemb, size_t size, int (* compar)(const void *, const void *)) |
string.h | 字符串處理,包括 1)內存管理函數 void * memset(void * dest, int c, size_t n), void * memcpy(void * dest, const void * src, size_t n), int memcmp(const void * s1, const void * s2, size_t n), void * memchr(const void * s, int c, size_t n) 2)字符串處理函數 char * strcat(char * deat, const char * src), char * strcpy(char * dest, const char * src), char * strncpy(char * dest, const char * src, size_t n), int strcmp(const char * s1, const char * s2), int strncmp(const char * s1, const char * s2, size_t n), size_t strlen(const char * s), char * strtok(char * s1, const char * s2), char * strstr(const char * s1, const char * s2), char * strpbrk(const char * s1, const char * s2), char * strchr(const char * s, int c), char * strrchr(const char * s, int c) |
math.h | 數學函數,包括 1)指數與對數 exp, pow, sqrt, log, log10 2)取整 ceil, floor 3)取絕對值 fabs |
ctype.h | 字符類測試,包括大小寫轉換 tolower(c), toupper(c) |
time.h | 時間和日期, clock_t clock(void), double difftime(time_t time1, time_t time0) |
error.h | 定義錯誤代碼,perror(const char * s) |
stddef.h | 一些常數、類型和變量 |
assert.h | 斷言,assert(int x), abort() 函數使程序異常終止 |
float.h | 浮點數運算 |
limits.h | 定義整數數據類型的取值范圍 |
locate.h | 本土化 |
setjmp.h | 非局部跳轉 |
signal.h | 信號 |
stdarg.h | 可變參數列表 |
其中stdlib.h庫有比較重要的排序和搜索函數,string.h庫有重要的字符串比較、連接、復制、找子串函數,對字符串匹配有重要作用。
2)qsort的形參有一個 void * , 該算法一個重點是指針的運用,這里我們說說void* 和 void**的區別
void*是一種特殊的指針類型,可用於存放任意對象的地址。void**即為void類型指針的指針,void***為void類型指針的指針的指針。*(void**)表示void指針的指針的解引用,表示一個指針,可用於存放一個地址;
cmp函數中形參類型是void *,表示的是傳入數組中某兩個比較值的地址,該題傳入的是后綴數組,數組中每個值都是一個字符串的首地址,故該值的地址即字符串地址的地址,又叫指針的指針,該對象說白了還是個地址,當然可以用void*存放。
在使用void*和void**時,需先將該類型轉換成具體類型,才能進一步引用。故將其轉換成char**表示char類型地址的地址,再將其解引用,就得到了字符的地址,表示一個字符串。
c語言指針偏移也需要注意⚠️
C語言指針偏移技巧(也是一個要注意的坑)