文字描述:
串,就是字符串的意思。在数据结构中,串的存储方式有三种:定长顺序存储表示、堆分配存储表示和块链存储表示:
定长顺序存储表示: 以一组地址连续的存储单元存放字符序列,按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区。超出预定义的串值会被舍弃,成为“截断”。
堆分配存储表示: 以一组地址连续的存储单元存放字符序列,但它们的存储空间是在执行时动态分配而得。
块链存储表示: 和线性表的链式存储结构类似,如下图,一个是结点大小为1的链表,一个是结点大小为4的链表;在链式存储方式中,结点大小的选择很重要,太小的话,虽然运算方便但是存储占用量大;如果太大的话,会太浪费空间。另外,它除了连接字串方便外,其他也不如前面两种存储方式灵活,一般都不用。
串的求子串的定位函数:返回子串在主串中的位置。
方法一,通用做法,如下图主串位置在匹配过程中可能会后退
方法二:模式匹配的一种改进算法KMP。主串匹配的下标一直前进,没有后退。关于这个算法,虽然KMP实现的代码很短,还是并不容易理解,可以参考下https://www.cnblogs.com/ZuoAndFutureGirl/p/9028287.html
方法三:优化后的KMP。按照方法二的KMP算法,在某些情况下是有缺陷的,比如主串为“aaabaaaab”, 子串为”aaaab”的时候,匹配时,当i=4、j=3时,S.ch[4] != T.ch[4],有next[j]指示还需进行i=4,j=3; i=4,j=2; i=4,j=1这3次比较。显然j=3、2、1都为’a’,和主串中i=4的比较结果都是一样的,可以将模式一气向右滑动4个字读的位置直接进行i=5、j=1的字符比较。
算法分析:
一般方法求子串在主串中的位置下标的时间复杂度为n*m
而KMP算法求子串在主串中的位置下标的时间复杂度为n+m, 另外在KMP中求串的next函数值的算法的时间复杂度为m。
代码实现:
串的定长顺序表示
1 // 2 // Created by Zhenjie Yu on 2019-04-14. 3 // 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #define DEBUG 10 #ifdef DEBUG 11 #include <stdarg.h> 12 #define LOG(args...) _log_(__FILE__, __FUNCTION__, __LINE__, ##args); 13 static void _log_(const char *file, const char *function, int line, const char * format, ...) 14 { 15 char buf[1024] = {0}; 16 va_list list; 17 va_start(list, format); 18 vsprintf(buf+strlen(buf), format, list); 19 sprintf(buf+strlen(buf), "\n"); 20 va_end(list); 21 printf(buf); 22 } 23 #else 24 #define LOG 25 #endif // DEBUG 26 27 #define MAX_STRING 255 28 typedef unsigned char SString[MAX_STRING+1]; //第一位表示串长 29 30 static int Concat(SString T, SString S1, SString S2){ 31 int uncut = -1; 32 if(S1[0]+S2[0] <= MAX_STRING){ 33 snprintf(T+1, S1[0]+1, "%s", S1+1); 34 snprintf(T+1+S1[0], S2[0]+1, "%s", S2+1); 35 T[0] = S1[0] + S2[0]; 36 uncut = 1; 37 }else if(S1[0] < MAX_STRING){ 38 snprintf(T+1, S1[0]+1, "%s", S1+1); 39 snprintf(T+1+S1[0], MAX_STRING-S1[0]+1, "%s", S2+1); 40 T[0] = MAX_STRING; 41 uncut = 0; 42 }else{ 43 snprintf(T+1, MAX_STRING+1, "%s", S1+1); 44 T[0] = MAX_STRING; 45 uncut = 0; 46 } 47 return uncut; 48 } 49 50 static int StrAssign(SString T, const char chars[]){ 51 memset(T, 0, MAX_STRING+1); 52 int len = strlen(chars); 53 if(len > MAX_STRING){ 54 return -1; 55 } 56 T[0] = len; 57 snprintf(T+1, len+1, "%s", chars); 58 return 0; 59 } 60 61 static int SubString(SString Sub, SString S, int pos, int len) 62 { 63 if(pos<1 || pos>S[0] || len<0 || len>(S[0]-pos+1)){ 64 return -1; 65 } 66 snprintf(Sub+1, len+1, S+pos); 67 Sub[0] = len; 68 return 0; 69 } 70 71 int main(int argc, char *arg) 72 { 73 SString S1, S2, T, Sub; 74 int ret = -1; 75 76 StrAssign(S1, "test123"); 77 LOG("设置串S1的值为%s, 长度为%d", S1+1, S1[0]); 78 79 StrAssign(S2, "abcdef"); 80 LOG("设置串S2的值为%s, 长度为%d", S2+1, S2[0]); 81 82 ret = Concat(T, S1, S2); 83 LOG("连接串S1和S2,并保存到变量T中,连接后T为%s, T的长度为%d", T+1, T[0]); 84 85 ret = SubString(Sub, T, 4, 6); 86 LOG("求主串T中,起始地址为4,长度为6的子串,子串结果保存咋变量Sub中; Sub的值为%s,长度为%d", Sub+1, Sub[0]); 87 return 0; 88 }