串(C语言实现)


串的逻辑结构和线性表极为相似,区别仅在于串的数据对象约束为字符集。这里主要介绍一下串的数据类型定义,存储结构,以及串的模式匹配算法——BF 算法和 KMP 算法。

1.串的数据类型定义

数据对象

  • D = { ai | ai ∈ CharacterSet, i = 1, 2, …, n, n ≥ 0 }

1.1 数据关系

  • R1 = { < ai, aj > | i < j, ai, aj ∈ D, i = 2, …, n }

1.2 基本操作

操作名称初始条件操作结果
StrAssign(&T, chars)chars 是字符串常量。生成一个其值等于 chars 的串 T。
StrCopy(&T, S)串 S 存在。由串 S 复制得串 T。
StrEmpty(S)串 S 存在。若 S 为空串,则返回 true;否则返回 false。
StrCompare(S, T)串 S 和 T 存在。若 S > T, 则返回值 > 0; 若 S = T, 则返回值 = 0; 若 S < T, 则返回值 < 0。
StrLength(S)串 S 存在。返回 S 的元素个数,称为串的长度。
ClearString(&S)串 S 存在。将 S 清为空串。
Concat(&T, S1, S2)串 S1 和 S2 存在。用 T 返回由 S1 和 S2 连接而成的新串。
SubString(&Sub, S, pos, len)串 S 存在,1 ≤ pos ≤ StrLength(S) 且 0 ≤ len ≤ StrLength(S) - pos + 1。用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串。
Index(S, T, pos)串 S 和 T 存在,T 是非空串,1 ≤ pos ≤ StrLength(S)。若主串 S 中存在和串 T 值相同的子串,则返回它在主串 S 中第 pos 个字符之后第一次出现的位置;否则函数值为 0。
Replace(&S, T, V)串 S, T 和 V 存在,T 是非空串。用 V 替换主串 S 中出现的所有与 T 相等的不重叠的子串。
StrInsert(&S, pos, T)串 S 和 T 存在,1 ≤ pos ≤ StrLength(S) + 1。在串 S 的第 pos 个字符之前插入串 T。
StrDelete(&S, pos, len)串 S 存在,1 ≤ pos ≤ StrLength(S) - len + 1。从串 S 中删除第 pos 个字符起长度为 len 的子串。
DestroyString(&S)串 S 存在。串 S 被销毁。

2.串的存储结构

2.1 串的顺序存储

// 串的顺序存储结构
#define Max_Size 255    //串的最大长度
typedef struct String{
    char ch[Max_Size+1];
    int length;
}SString;

其中,Max_Size 表示串的最大长度,ch 是存储字符串的一维数组,每个分量存储一个字符,length 表示字符串的当前长度。为了符合习惯,一般将下标为 0 的数组闲置不用,尽量从 1 开始。

2.2 串的链式存储

// 串的链式存储
typedef struct LNode{
    char *ch;
    struct LNode *next;
}LinkList;

typedef struct {
    LinkList *head,*tatial;     //串的头尾指针
    int length;
}LString;

顺序串的插入和删除操作不方便,需要移动大量的字符。因此, 可采用单链表方式存储串。

3.串的模式匹配算法

子串的定位运算通常称为串的模式匹配或串匹配。此运算的应用非常广泛,比如在搜索引擎、拼写检查、语言翻译、数据压缩等应用中, 都需要进行串匹配。
串的模式匹配设有两个字符串 S 和 T, 设 S 为主串,也称正文串;设 T 为子串,也称为模式。在主串 S 中查找与模式 T 相匹配的子串,如果匹配成功, 确定相匹配的子串中的第一个字符在主串 S 中出现的位置。
著名的模式匹配算法有 BF 算法和 KMP 算法,下面详细介绍这两种算法。

3.1BF 算法

BF 算法是经典的暴力解法,子串 T 与 S 逐个匹配,相同就往后走,不相同就回溯。这个算法最好情况下时间复杂度为 O(n+m),最坏情况下为 O(m*n)。具体代码如下

// BF算法
int Index_BF(SString S, SString T, int pos){
// 返回模式T在主串s中第pos个字符开始第一次出现的位置。若不存在, 则返回值为0
    int i = pos;
    int j = 1;
    while (i < S.length && j < T.length){
        if(S.ch[i] == T.ch[j]){
            i++;
            j++;
        }else{
            i = i - j + 2;
            j = 1;  //回溯
        }
    }
    if(j > T.length){
        return i - T.length;    //匹配成功
    }
    return 0;
}

3.2KMP 算法

KMP 算法可以在 O(n+m)的时间数量级上完成串的模式匹配操作。其改进在千:每当一趟匹配过程中出现字符比较不等时,不需回溯 l 指针,而是利用已经得到的”部分匹配" 的结果将模式向右"滑动“ 尽可能远的一段距离后,继续进行比较。

// 获取next数组
void Get_Next(SString T, int next[]){
    int i = 1;
    next[1] = 0;
    int j = 0;
    while (i < T.length){
        if(j == 0 || T.ch[i] == T.ch[j]){
            i++;
            j++;
            next[i] = j;
        }else{
            j = next[j];
        }
    }
}

//KMP算法
int Index_KMP(SString S, SString T, int pos, int next){
    int i = pos;
    int j = 1;
    while (i <= S.length && j <= T.length){
        if(S.ch[i] == T.ch[j]){
            i++;
            j++;
        }
        else{
            j = next[j];
        }
    }
    if(j > T.length){
        return i - S.length;
    }
    return -1;
}

前面定义的 next 函数在某些情况下尚有缺陷;例如模式"aaaab" 在和主串"aaabaaaab"匹配时,当 i = 4 、j= 4 时 s.ch [ 4] -:t:- t.ch [ 4] , 由 next(j) 的指示还需进行 i = 4 、j= 3, i = 4 、j= 2, i = 4 、j=l 这 3 次比较。因此,需要我们对 next 进行修正。

// 修正next数组
void Get_Nextval(SString T, int nextval[]){
    int i = 1;
    nextval[0] = 0;
    int j = 0;
    while (j <= T.length){
        if(j == 0 || T.ch[i] == T.ch[j]){
            i++;
            j++;
            if(T.ch[i] == T.ch[j]){
                nextval[i] = nextval[j];
            }
            else{
                nextval[i] = j;
            }
        }
        else{
            j = nextval[j];
        }
    }
}

以上就是串的全部内容,如有错误请联系 QQ:303623518

posted @ 2024-09-23 19:11  写代码的大学生  阅读(43)  评论(0编辑  收藏  举报  来源