串(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