算法与数据结构(c语言)——串

串的一些个存储结构:

  • 顺序存储结构的串
#define MAXSIZE 255
// 0下标位置的长度存放这个串的长度
typedef unsigned char String[MAXSIZE+1];
  • 链式存储的串:
#define MAXSIZE 1024

typedef struct{
    char *chars;
    //定义一个串长度
    int length;
}String;
  • 块链式存储结构的串:一个或若干个字符形成一个块,并占用一个节点,串为这样的节点相连
// 每个块的大小
#define CHUNKSIZE 128
typedef struct Chunk{
    char charArr[CHUNKSIZE];

    struct Chunk *next;
}Chunk;
typedef struct{
    Chunk *head,tail;
    // 当前串长度
    int curlen;
}String;

本篇博文关于串的操作都是 链式存储结构的。

串的操作

  • 初始化一个串:
String* InitString(){
    String *str = (String *)malloc(sizeof(String));
    str->chars = (char *)malloc(sizeof(char) * MAXSIZE);
    str->length = 0;
    return str;
}
  • 字符数组->串
void charArr2String(String *s,char *string){
    int strLength = strlen(string);

    if(strLength >= MAXSIZE){
        s->chars = (char *)malloc(sizeof(char) * MAXSIZE * 2);
    }

    // 设置串的当前长度
    s->length=strLength;
    s->chars = string;
}
  • 判断串是否为空
bool StringEmpty(String *str){
    return str->length == 0;
}
  • 截取子串
/*
从s串中指定位置开始取长度为len的字串,用sub返回。
sub:返回的字串
s:主串
pos:指定下标
len:要截取的长度
*/
bool SubString(String *sub,String *s,int pos,int len){

    bool flag = pos < 0||pos >= s->length||len < 1||pos + len > s->length;
    if(flag){
        return false;
    }

    for(int i = pos; i < (pos + len); i++){
        sub->chars[i-pos] = s->chars[i];
    }

    sub->chars[len] = '\0';
    sub->length = len;

    return true;
}
  • 比较俩个串的大小
int StringCompare(String *s1,String *s2){
    if(StringEmpty(s1)){
        return -1;
    } else(StringEmp2y(s2)){
        return 1;
    }

    int i=0;
    while(i < s1->length && i < s2->length){
        if(s1->chars[i] == s2->chars[i]){
            i++;
            continue;
        } else{
            return s1->chars[i] - s2->chars[i];
        }

    }
    return 0;
}
  • 在主串s中第pos个字符后查找与t串相等的字串
int Index(String *s,String *t,int pos){
    int i;
    String *sub = InitString();
    i = pos;
    if(pos >= s->length || pos < 0){
        return -1;
    }

    while(i <= s->length - t->length){
        SubString(sub,s,pos++,t->length);

        if(StringCompare(sub,t) == 0){
            return i;
        } else{
            // 若取出的串不相等,继续
            i++;
        }
    }
    return -1;
}
  •  在主串s中第pos个字符后查找与t串相等的字串(不考虑用串的其他操作,只用基本的数组来实现同样的算法)
int Index_Arr(String *s,String *t,int pos){
    int j = pos;
    int i = 0;

    if(pos >= s->length || pos < 0){
        return -1;
    }
    while(j < s->length && i< t->length){
        if(s->chars[j] == t->chars[i]){
            ++i;
            ++j;
        } else{
            j = j-i+1;
            i = 0;
        }
    }
    if(i >= t->length){
        return j - t->length;
    }
    return -1;
}
/*
不足:效率低

看这种情况:
s1:000000000000001
s2:001
串2的前两个字符,和串1的字符1之前都匹配,需要匹配13*3才可能匹配上,效率低下

所以就有几个牛人,构思出了一个算法(KMP算法),来提高效率。
*/

 kmp算法代码实现:获取模式串的next数组

/*
通过循环,不断的将前缀的单个字符与后缀的单个字符进行比较,
    若相等,则将其存储到数组中。改变记录前缀和后缀的下标变量。
    不相等,将数组中的值给前缀的下标记录。
*/
void getNextArr(String str,int *nextArr){
    // i表示前缀的单个字符,j表示后缀的单个字符
    int i,j;
    i = -1;
    j = 0;
    nextArr[0] = -1;

    // 这个后缀的单个字符的下标是不会回溯的,只有前缀的单个字符的下标会进行回溯。
    while(j < str.length - 1){
        if(i == -1 || str.chars[i] == str.chars[j]){
            nextArr[++j] = ++i;
        } else {
            // 若值不相等,则i值进行回溯
            i = nextArr[i];
        }
    }
}

 那么在使用kmp算法之后的index应该如何写呢

int Index_KMP(String S, String P, int pos){

    int next[255];
    getNextArr(P, next);

    int i = pos;  // S 的下标
    int j = 0;  // P 的下标
    int s_len = S.length;
    int p_len = P.length;

    while (i < s_len && j < p_len){
        if (j == -1 || S.chars[i] == P.chars[j]){
            // P 的第一个字符不匹配或 S[i] == P[j]
            i++;
            j++;
        }
        else{
            j = next[j];
            // 当前字符匹配失败,退回到合适位置
        }
    }
    if (j == p_len)  // 匹配成功
        return i - j;
    return -1;
}

/*
那么这个算法有什么不足或者说缺陷呢:
    在aaaabcdefghijk中找
    串aaaaab
    当出现不匹配(a和b)的时候,也就是next[j]等于5,
    进行回溯,next[j]成了4,不匹配,继续回溯,直到第next[j]等于0。
    b不相等的时候回溯了多次,因为前面5个字符都是相等的,那么这样一来之前的判断回溯就都是多余的了
*/

 改进一下KMP模式匹配算法

void getNextValArr(String str,int *nextArr){
    int i,j;
    i = -1;
    j = 0;
    nextArr[0] = -1;

    while(j < str.length - 1){
        if(i == -1 || str.chars[i] == str.chars[j]){
            // 修改的结果:

            if (str.chars[++i] != str.chars[++j]){
                // 当前字符和前缀字符不同的时候,当前的i为next在j位置的值。
                nextArr[j] = i;
            } else {
                // 前缀和当前字符相同将前缀字符的nextArr的值赋值给nextArr在i位置的值
                nextArr[j] = nextArr[i];
            }
        } else {
            // 若值不相等,则i值进行回溯
            i = nextArr[i];
        }
    }
}
/*
总结改进过的kmp:它是在计算花出next值的同时,
如果a位字符与它next值指向的b位字符相等,则该a位的nextArr就存储的b位的nextArr值
如果不等,则该a位的nextArr就是它自己a位的nextArr的值
*/

在两串中找出最大长度的共同串(可指定长度),若存在,返回这个串

void Intersection(String *res,String *s1, String *s2, int len){
    if(StringEmpty(s1) ||StringEmpty(s2) || len > s1->length || len > s2->length || len == 0){
        return;
    }

    // 从第i个位置开始拿子串
    // 从第j个位置开始找
    int i,j;

    // 要取的字串长度
    int tagetLen = s2->length;

    // 存储s2的子串
    String *s2sub = InitString();
    // 给子串赋初始值
    SubString(s2sub, s2, 0, tagetLen);

    while(tagetLen >= len){
        i = 0;
        while(i <=s2->length - tagetLen){
            SubString(s2sub,s2,i,tagetLen);
            j = 0;
            // 取出字串到s1串中查找
            while(j <= s1->length - s2sub->length){
                int index = Index_KMP(s1,s2sub,j++);
                if(index != -1){
                    // 找到后进行赋值,然后结束循环
                    res->chars = s2sub->chars;
                    res->length = tagetLen;
                    return;
                }
            }
            // 没找着从下一个位置开始拿子串
            i++;
        }

        // 长度为tagetLen的所有字串在串s1中没找到,将要取的字符串长度自减
        tagetLen--;
    }
}

 ps:一篇博文助你理解KMP

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值