当前位置 博文首页 > XSES_yasuoman的博客:数据结构——串

    XSES_yasuoman的博客:数据结构——串

    作者:[db:作者] 时间:2021-08-31 22:22

    • 考研中默认每个字符占用 1B (一个字节)= 8 bit
    • 一个汉字占用 2B = 16 bit

    串的存储结构

    1. 定长顺序存储表示

    #define MAXLEN 255
    typedef struct{
        char ch[MAXLEN];
        int length;			// 注意 ch[0] 不存储任何字符,所以计算长度需注意!!
    }SString;
    
    • ch 数组下标 0 不存储字符,从 ch[1] 开始存储字符,使用 length 存储串的长度,而不是在 ch[0] 中存储,因为其只能存储 0~255 长度。


    2. 堆分配存储表示

    typedef struct{
        char *ch;
        int length;
    }HString;
    
    HString str = (char *)malloc(sizeof(char) * initsize);
    free(str);
    

    使用 malloc 在堆上申请空间,使用结束可以用 free 销毁


    3. 块链存储表示

    typedef struct StringNode {
        char ch[4];
        struct StringNode *next;
    }StringNode, *String;
    
    • StringNode 表示结点,String 强调字符串的起始地址。
    • 在结构体中使用 ch[4] 是为了增加存储密度,因为一个指针的大小为 4B,若一个结点只存储一个字符,则密度过低了。



    串的基本操作

    赋值: StrAssign(&T, chars)

    复制:StrCopy(&T, S)

    判空:StrEmpty(S)


    比较:StrCompare(S, T)

    int StrCompare(SString S, SString T) {
        for (int i = 1; i < S.length; i ++) {
            if (S.ch[i] != T.ch[i])		// 不相等时定结果
                return S.[i] - T.[i];	// 字符比较
        }
        return S.length - T.length;		// 如果两者前面的字符都对应相等,则比较长度。长的字符串大。
    }
    


    求串长:StrLength(S)


    求子串:SubString(&Sub, S, pos, len)

    // 静态定长顺序存储结构
    bool SubString(SString &Sub, SString S, int pos, int len) {
        if (pos + len - 1 > S.length)					// 子串范围越界?
            return false;
        for (int i = pos, i < pos + len - 1; i ++) {	// 从 pos 到 pos + len - 1 都需要赋值
            Sub.ch[i - pos + 1] = S.ch[i];				// Sub 从下标 1 开始赋值。
        }
        Sub.length = len;								// 不忘 Sub 的length 属性。
        return true;
    }
    


    串联接:Concat(&T, S1, S2)

    定位操作:Index(S, T)

    王道书:

    思路:求 S 中与 T 长度一样长的子串,依次比较。

    int Index(SString S, SString T) {
        int i = 1, lenS = StrLength(S), lenT = StrLength(T);
        SString sub;		// 暂存子串
        while (i <= lenS - lenT + 1) {
            SubString(sub, S, i, lenT);		// 在 S 中 i 位置找长度为 lenT 的子串
            if (StrCompare(sub, T) != 0)
                i++;
            else
                return i;
        }
        return 0;
    }
    


    MHH:

    依次比较,其实无意中发现了模式匹配呀~

    int Index(SString S, SString T) {
        int Ti = 1;	// 短串下标
        int Si;		// 长串下标
        // 长串 S 遍历至末尾,或 T 匹配到末尾则结束
        for (Si = 1; Si <= StrLength(S) || Ti <= StrLength(T); Si++){
            if (S.ch[Si] == T.ch[Ti])		// 匹配到,Ti Si 都加 1
                Ti ++;
            else							// 未匹配则 Ti重置为 1,Si 加1
                Si = Si - Ti + 2;			// Si 从下一个开始重新匹配
                Ti = 1;
        }
        if (Ti = StrLength(T) + 1)
            return Si - StrLength(T);
        else
            return -1;
    }
    


    清空操作:ClearString(&S)

    销毁串:DestroyString(&S)



    串的模式匹配

    简单的模式匹配算法——主串遍历

    int Index(SString S, SString T) {
        int Si = 1, Ti = 1;
        while (Si <= S.length && Ti <= T.length) {
            if (S.ch[Si] == T.ch[Ti]){
                Si ++;
                Ti ++;					// 比较后续
            }else {
                Si = Si - Ti + 2;		// 指针后退并重新开始匹配
                Ti = 1;
            }
        }
        if (Ti = T.length + 1)
            return Si - T.length;
        else
            return 0;
    }
    



    改进的模式匹配算法——KMP 算法

    主串指针不需要回溯,子串(模式串)向后移动

    eg:主串:a b a b a b c d d,子串:a b a b c,第一次匹配:

    1 2 3 4 5 6 7 8 9 	// 匹配到第 5 位失败,则前 4 位 主串子串相等。
    a b a b a b c d d	// 为了减少匹配,需要找到子串的前 4 位中的重叠部分,使得尽可能将子串后移多位。
    a b a b c			// 
      a b a b c			// 移动 1 位,第一个便不匹配,则进行 2 位移动,
        				// 此时最多需要匹配 5 - 1 - 2 次,而它们刚好都匹配。于是从 2 + 1 开始匹配。
        a b a b c		// 向后能移动的最大位数是 2,则移动 2 位后,下一次从子串的位置 3 开始匹配
    


    next[Sub.length + 1];		// next 数组存储,当该位置匹配失败时,子串指针移动到哪个位置进行匹配
    
    • next 的长度为 Sub.length + 1 的原因

      当第一个元素不匹配时,需要将主串和子串的指针都自增 1,但其他元素不匹配时,做的工作都是,令 子串指针等于 next[i],随后主串指针自增,所以需要将 next[1] 置为 0,此时将 主串指针 和 子串指针都加 1。


    已经求得 next 数组情况下,进行匹配:

    int Index_KMP(SString S, SString T, int next[]){
        int Si = 1, Ti = 1;		// S 串 和 T 串的位置
        while (Si <= S.length && Ti <= T.length) {
            if (Ti == 0 || S.ch[Si] == T.ch[Ti]) {
                Si++;
                Ti++;
            }else {
                Ti = next[Ti];
            }
        }
        if (Ti > T.length)
            return Si - T.length;
        else
            return 0;
    }
    


    求 next 数组:

    • next[1] 无脑为 0,next[2] 无脑为 1。
    • 在失配元素前划线,将子串依次后移,找到第一个次能匹配时子串在线后面的位置,即为 next 对应的值,若匹配不到,则 next 为 1。


    挖坑:

    next 数组代码:

    next 数组优化:



    cs
    下一篇:没有了