数据结构 - 串

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]];
        }
    }
}
posted @ 2022-10-31 23:15  水三丫  阅读(135)  评论(0编辑  收藏  举报