数据结构 - 串
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]];
}
}
}