数据结构复习:串的基本操作实现、KMP算法

#include <stdio.h>
#include <stdlib.h>

#define MAXLEN 255 //串的最大长度
//动态数组实现(堆分配存储)
typedef struct {
    char *ch; // 按串长分配存储区,ch指向串的基地址
    int length; // 串的长度
} HString;

//初始化
bool InitHString(HString &S) {
    //手动分配一片连续空间,释放需要手动 free
    S.ch = (char *) malloc(MAXLEN * sizeof(char));
    S.length = 0;
}

//串的链式存储
//实现 1 个结点存储一个字符    存储密度第,每存储一个字符,字符占1
//typedef struct StringNode{
//    char ch;//每个结点存储一个字符占1B,指针占4B
//    struct StringNode *next;//下一个结点指针
//}StringNode,*String;
//实现2:每个结点存多个字符
typedef struct StringNode {
    char ch[4];//每个结点存多个字符
    struct StringNode *next;//下一个结点指针
} StringNode, *String;

//静态数组实现(定长顺序存储)
typedef struct {
    char ch[MAXLEN]; //每个分量存储一个字符
    int length;// 串的长度
} SString;

//判断空串
bool StrEmpty(SString S) {
    return S.length == 0;
}

//求串长 返回串S的元素个数
int StrLength(SString S) {
    return S.length;
}

//清空串
void ClearString(SString &S) {
    S.length = 0;//逻辑上删除
}

//销毁串
void DestroyString(SString &S) {
    ClearString(S);
    //离开作用域 等待系统自动回收
}

void InitSString(SString &S) {
    S.length = 0;
}

//求子串 用 Sub返回 串S的 第 pos 个字符起长度为 len 的子串
//比如 wangdao  获取 gda 的子串
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; ++i) {
        Sub.ch[i - pos + 1] = S.ch[i];
    }
    Sub.length = len;
    return true;
}

//打印串
void PrintSString(SString S) {
    for (int i = 1; i <= S.length; ++i) {
        printf("%c", S.ch[i]);
    }
    printf("\n");
}

//比较 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;
}
定位操作。若主串 S 中 存在与 串 T 相同的子串,返回它在主串 S 中第一次出现的位置;否则,返回0
使用 SubString 和 StrCompare 实现
//
//int Index(SString S,SString T) {
//    int pos = 1, n = StrLength(S), m = StrLength(T);
//    SString sub;//用于暂存子串
//    InitSString(sub);
//    while (pos < n-m + 1) {
//        SubString(sub,S,pos,m);
//        if(StrCompare(sub,T) !=0) pos++;
//        else return pos;//返回子串在主串中的位置
//    }
//    return 0;
//}

//朴素的模式匹配算法(简单模式匹配算法)
//如果 主串 S 的长度为 n,模式串 T的长度为 m
//最好情况下模式串在主串的最前面,即需要比较 m 次,最好时间复杂度 为O(m)
//最坏情况需要 (n-m+1)*m 次比较,最坏时间复杂度 O((n-m+1)*m
int Index(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]) {
            i++;
            j++;//继续比较后面的字符
        } else { // 当前子串不匹配
            k++;
            i = k;
            //i = (i-(j-1)) + 1 //模式串回退j-1,主串也回退j-1,同时主串要再+1
            j = 1;//模式串
        }
    }
    if (j > T.length) { // 模式串匹配完成
        return k;
    } else { //主串字符耗尽,而模式串没有匹配完
        return 0;
    }
}
//前缀:包含第一个字符,但不包含最后一个字符
//后缀:包含最后一个字符,单不包含第一个字符
//最长公共前后缀:即前缀和后缀相同的字符串,不包含当前字符串 即 abcdefg 不是 abcdefg 的公共前后缀
//例 ab :不存在;
// aba:不存在
// aa:a
// abab:ab
//abcdebabcd:abcd

//求next数组,即最长的公共前后缀 +1
void get_next(SString T, int next[]) {
    //ababab
    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];
        }
    }
}
//求 nextval数组

void get_nextval(SString T, int next[], int nextval[]) {
    nextval[1] = 0;
    for (int i = 2; i <= T.length; ++i) {
        if (T.ch[next[i]] == T.ch[i]) {//如果当前字符T.ch[i]和当前的 以当前字符对应的 next数组下标的字符 T.ch[next[i]] 相等
            nextval[i] = nextval[next[i]];//则当前字符的 nextval[i] = nextval[next[i]]
        } else {
            nextval[i] = next[i];//否则当前字符 nextval[i] 等于 next[i]
        }
    }
}

//KMP算法
int Index_KMP(SString S, SString T) {
    int i = 1, j = 1;
    int next[T.length + 1];
    get_next(T, next);//求模式串的 next 数组
    while (i <= S.length && j <= T.length) {
        if (j == 0 || S.ch[i] == T.ch[j]) {
            i++;
            j++;
        } else {
            j = next[j];
        }
    }
    if (j > T.length) {
        return i - T.length;//匹配成功
    } else {
        return 0;
    }
}


//KMP算法改进 nextval数组
int Index_KMP_nextval(SString S, SString T) {
    int i = 1, j = 1;
    int next[T.length + 1];
    get_next(T, next);//求模式串的 next 数组
    int nextval[T.length + 1];
    get_nextval(T, next, nextval);
    while (i <= S.length && j <= T.length) {
        if (j == 0 || S.ch[i] == T.ch[j]) {
            i++;
            j++;
        } else {
            j = nextval[j];
        }
    }
    if (j > T.length) {
        return i - T.length;//匹配成功
    } else {
        return 0;
    }
}

int main() {

    int pos = 0;
    int len = 0;
    SString S;
    InitSString(S);
    S.ch[1] = 'w';
    S.length++;
    S.ch[2] = 'a';
    S.length++;
    S.ch[3] = 'n';
    S.length++;
    S.ch[4] = 'g';
    S.length++;
    S.ch[5] = 'd';
    S.length++;
    S.ch[6] = 'a';
    S.length++;
    S.ch[7] = 'o';
    S.length++;

    printf("打印串S:\n");
    PrintSString(S);
    SString Sub;
    InitSString(Sub);
    //求wangdao 的子串 dao
    pos = 4, len = 3;
    if (SubString(Sub, S, pos, len)) {
        printf("获取S子串,pos:%d,len:%d成功\n", pos, len);
        printf("打印S的子串Sub:\n");
        PrintSString(Sub);
    } else {
        printf("获取S子串,pos:%d,len:%d失败\n", pos, len);
    }
    SString T;
    InitSString(T);
    T.ch[1] = 'w';
    T.length++;
    T.ch[2] = 'a';
    T.length++;
    T.ch[3] = 'n';
    T.length++;
    T.ch[4] = 'g';
    T.length++;
    T.ch[5] = 'd';
    T.length++;
    T.ch[6] = 'a';
    T.length++;
    T.ch[7] = 'a';
    T.length++;
    printf("打印串T:\n");
    PrintSString(T);
    int result = StrCompare(S, T);
    if (result > 0)
        printf("S>T\n");
    else if (result == 0)
        printf("S=T\n");
    else if (result < 0)
        printf("S<T\n");

    T.ch[1] = 'd';

    T.ch[2] = 'a';
    T.ch[3] = 'o';
    T.length = 3;
    T.ch[4] = 'a';
//    T.length = 4;
//    T.ch[3] ='o';
//    T.length = 3;

    printf("打印串S:\n");
    PrintSString(S);
    printf("打印串T:\n");
    PrintSString(T);
//    pos = Index(S,T);
//pos = Index_KMP(S,T);
    pos = Index_KMP_nextval(S, T);
    if (pos != 0) {
        printf("子串T在主串S中的位置为%d\n", pos);
    } else {
        printf("子串T不是主串S的子串\n");
    }
//    T.ch[4] = 'a';


    SString T1;
    InitSString(T1);
    T1.ch[1] = 'G';
    T1.ch[2] = 'O';

    T1.ch[3] = 'O';
    T1.ch[4] = 'G';
    T1.ch[5] = 'L';
    T1.ch[6] = 'E';
    T1.length = 6;
    int T1_next[T1.length+1];
    get_next(T1,T1_next);

    int T1_nextval[T1.length+1];
    get_nextval(T1,T1_next,T1_nextval);
    printf("\n");
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值