数据结构 - 串
6、串
串的定义
串,即字符串(string)是由零个或多个字符串组成的有限序列。
术语:
- 子串:串的任意个连续的字符组成的子序列
- 主串:包括子串的串
- 字符在主串中的位置:字符在串中的序号
- 子串在主串中的位置:子串的第一个字符在主串中的位置
注意:这里的位置是从1开始,而不是0
6.1、串的顺序存储
串的静态存储
#define MaxSize 255 typedef struct { char ch[MaxSize];//静态的字符数组 int length;//字符串的长度 }SString;
串的动态存储
typedef struct { char * ch;//字符的起始地址 int length;//字符串的长度 }HString; //初始化 int InitHString(HString * s){ &s.ch = (char *)malloc(sizeof(char)*MaxSize); &s.length = 0; return 1; }
6.2、串的链式存储
一个串值存储一个字符
typedef struct StringNode{ char ch;//1个字节 struct StringNode *next;//四个字节 }StringNode,*String;
缺点:存储密度低,字符1B,next指针4B;
一个串值存储多个个字符
typedef struct StringNode{ char ch[4];//4个字节 struct StringNode *next;//四个字节 }StringNode,*String;
测试
#include <stdio.h> #include <stdlib.h> #define MaxSize 255 #define true 1 #define false 0 #define boolean int typedef struct SString{ char ch[MaxSize];//字符串 int length;//字符串的长度 }SString; //把ch赋值给S的字符串 boolean StrAssign(SString *S,char ch[]){ S->ch[0] = ' '; for(int i = 1;i <= S->length;i++){ S->ch[i] = ch[i-1]; } return true; } //字符串判断是否为空 boolean StrEmpty(SString S){ if(S.length == 0) return true; else return false; } //清空字符串 boolean ClearString(SString *S){ if(!StrEmpty(*S)){ *S->ch = ""; S->length = 0; } return true; } //求字符串长度 int StrLength(SString S){ return S.length; } //复制操作 boolean StrCopy(SString *T,SString S){ T->ch[0] = ' '; if(!StrEmpty(S)){ for(int i=1;i<=S.length;i++){ T->ch[i] = S.ch[i]; } T->length = S.length; } return true; } //串连接;用T返回S1 和 S2连接的新串 时间复杂度O(max(S1.length,S2.lengtt)) boolean Concat(SString *T,SString S1,SString S2){ T->ch[0] = ' '; if(S1.length+S2.length > MaxSize) return false;//长度超出边界 int length = S1.length>S2.length?S1.length:S2.length;//选择字符串大的那个长度 if(!StrEmpty(S1) || !StrEmpty(S2)){ for(int i=1;i<=length;i++){//循环赋值 if(i<=S1.length){ T->ch[i] = S1.ch[i];//赋值S1 } if(i+S1.length <= S2.length + S1.length){ T->ch[i+S1.length] = S2.ch[i];//赋值S2 } } T->length = S1.length + S2.length;//长度赋值为S1+S1的长度 } return true; } //求子串 boolean SubString(SString *Sub,SString S,int pos,int len){ if(pos + len - 1 > S.length || pos < 1) return false;//越界了 Sub->ch[0] = ' '; for(int i=pos;i<len+pos;i++){ Sub->ch[i-pos+1] = S.ch[i]; //子串赋值 } Sub->length = len; //子串的长度 return true; } //比较操作 返回大于0:表示S>T;等于0,S=T;返回小于0,S<T int StrCompare(SString S,SString T){ for(int i = 1;i <= S.length && i <= T.length;i++){ if(S.ch[i] != T.ch[i]){ return S.ch[i] - T.ch[i]; } } return S.length - T.length; } //定位操作,返回串T,在S中第一次出现的位置 int Index(SString S,SString T){ int i = 1,n = StrLength(S),m = StrLength(T);//记录两个串的大小 SString Sub; while(i <= n - m + 1){//依次寻找子串比对 SubString(&Sub,S,i,m); if(StrCompare(Sub,T) == 0){ return i;//比对成功就返回位置 }else{ i++;//失败就++ } } return 0;//全部寻找完,都没有匹配的,就返回0 } //定位操作(朴素模式的匹配算法) int Index2(SString S,SString T){ int k = 1; int i = k,j=1; while(i <= S.length && j <= T.length){ if(S.ch[i] == T.ch[j]){ j++; i++; }else{ j = 1; i = ++k; } } if(j > T.length) return k; else return 0; } //模式字符串的next数组 void get_next(SString T,int next[]){ int i=1,j=0; next[1] = 0; while(i < T.length){ if(j == 0 || T.ch[i] == T.ch[j]){ ++i;++j; next[i] = j; }else{ j = next[j]; } } } //KPM算法 int KPM_Index(SString S,SString T){ int i = 1,j=1; int *next = (int *)malloc(sizeof(int)*(T.length+1)); get_next(T,next); while(i <= S.length && j <= T.length){ if(j==0 || S.ch[i] == T.ch[j]){ j++; i++; }else{ j = next[j]; } } if(j > T.length) return i-T.length; else return 0; }
int main(){ SString S; char ch[] = "abcdefghijklnm"; S.length = 14; StrAssign(&S,ch); printf("字符串: %s \n",S.ch); printf("字符串长度:%d \n",StrLength(S)); printf("字符串是否为空:%d \n",StrEmpty(S)); SString T; StrCopy(&T,S); printf("字符串T: %s \n",T.ch); printf("字符串长度: %d \n",T.length); printf("字符串T是否为空:%d \n",StrEmpty(T)); SString S1; char ch1[] = "abcdefghijklnm"; S1.length = 14; StrAssign(&S1,ch1); SString S2; char ch2[] = "rrrrr"; S2.length = 5; StrAssign(&S2,ch2); SString T1; Concat(&T1,S1,S2); printf("字符串T1: %s \n",T1.ch); printf("字符串T1长度: %d \n",T1.length); printf("字符串T1是否为空:%d \n",StrEmpty(T1)); ClearString(&S); printf("字符串: %s \n",S.ch); printf("字符串是否为空:%d \n",StrEmpty(S)); //求子串 SString Sub; SubString(&Sub,T1,5,5); printf("字符串Sub: %s \n",Sub.ch); printf("字符串Sub长度: %d \n",Sub.length); printf("字符串Sub是否为空:%d \n",StrEmpty(Sub)); SString C1; SString C2; char str1[] = "abcde"; C1.length = 5; StrAssign(&C1,str1); char str2[] = "bcd"; C2.length = 3; StrAssign(&C2,str2); printf("C1 > C2 : %d \n",StrCompare(C1,C2)); printf("C2 是 C1的子串吗?是返回位置,不是返回0:%d \n",Index2(C1,C2)); printf("KPM算法 : C2 是 C1的子串吗?是返回位置,不是返回0:%d \n",KPM_Index(C1,C2)); return 0; } //结果: 字符串: abcdefghijklnm 字符串长度:14 字符串是否为空:0 字符串T: abcdefghijklnm 字符串长度: 14 字符串T是否为空:0 字符串T1: abcdefghijklnmrrrrr 字符串T1长度: 19 字符串T1是否为空:0 字符串: 字符串是否为空:1 字符串Sub: efghi 字符串Sub长度: 5 字符串Sub是否为空:0 C1 > C2 : -1 C2 是 C1的子串吗?是返回位置,不是返回0:2 KPM算法 : C2 是 C1的子串吗?是返回位置,不是返回0:2
朴素模式的匹配算法
将主串中与模式串相同长度的连续子串弄出来,和模式串进行比较,不同就换下一个,相同就返回
最好时间复杂度为:O(n)
最坏时间复杂度为:O(nm)
KPM字符串匹配算法
当子串和模式串不匹配的时候,主串指针i不回溯,模式串指针 j = next[j];
next数组手算方法:当第j个字符匹配失败,由前1~j-1个字符组成的串记为S,则:next[j]=S的最长相等前后缀长度+1;特别的next[1]=0
算法的平均时间复杂度为:O(m+n)
KPM优化算法:当子串和模式串不匹配时,j=nextval[j]
//优化模式字符串的next数组 void get_nextval(SString T,int next[]){ int i=1,j=0; next[1] = 0; while(i < T.length){ if(j == 0 || T.ch[i] == T.ch[j]){ ++i;++j; next[i] = j; }else{ j = next[j]; } } for(int j = 2;j<=T.length;j++){ if(T.ch[next[j]] == T.ch[j]){ next[j] = next[next[j]]; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix