题目描述:妞妞有两个字符串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语言指针偏移技巧(也是一个要注意的坑)