String Structures
五月 29, 2021
串
字符串的定义:由==零个==或==多个==字符组成的==有限序列==(记作:s=“a1 a2 … an”(n≥0))
- 空串(null string):零个字符(n为串的长度)
- 空格串:只含有(多个)空格
- 子串、主串:包含子串的串称主串
- 串的抽象数据类型:P107
串的比较:逐位比较
- 标准ASCII码:128(7位二进制组成)
- 扩展ASCII码:256(8位二进制组成)
- Unicode码:216(16位二进制组成 ≈ 6.5w)【前256位跟ASCII码相同】
串的存储结构
- 顺序存储结构:用一组==地址连续==的存储单元来存储串中的字符序列【定长数组】
- a[0]:数组第一位保存预定义最大串长度
- “\0”:串值终结(遍历长度:
for(i ; a[i] != '\0' ; i++)
) - 弊端:超出数组长度Maxsize,发生上溢出,截尾
- 动态分配:由自由存储区“==堆==”调用C语言的malloc()、free(),进行动态分配管理
- 链式存储结构:一个结点对应一个字符或多个字符,“#”补全(方便连接串,存储性能不如顺序结构)
- 顺序存储结构:用一组==地址连续==的存储单元来存储串中的字符序列【定长数组】
串的模式匹配算法(子串的定位)
朴素的模式匹配算法(主串(大循环)、子串(小循环)的循环遍历,比较指针回溯)【等概率原则:O(n+m)】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37/*
T[0]、S[0]:存放串长度
1. 定义索引主串的指针i、索引子串的指针j=1(从头开始)
2. 判断:当主串指针小于等于主串长度 且 子串指针小于等于子串长度
1. 判断:数据相等
a. 主串指针、子串指针++
2. 数据不等
a.主串指针退回上次匹配首位的下一位
b.子串指针重置 1
3. 判断:子串指针超出自串长度
遍历完成,返回位置
4. 无结果,返回0
*/
int Index(String S,String T,int pos)
{
int i=pos;
int j=1; //1
while(i<=S[0] && j<=T[0]) //2
{
if(S[i]==T[j]) //2.1
{
i++;
j++;
}
else //2.2
{
i = i-j+2; //2.a
j=1; //2.b
}
}
if(j>T[0])
{
return i-T[0]; //3
}else{
return 0;
}
}KMP模式匹配算法(时间复杂度O(n+m))
最长模式串:前缀模式串 == 后缀模式串(小于整个模式串长度)
增加next数组进行==相似度查询==:j值 的大小取决于当前字符之前的串前后缀的相似度
next[j] 数组:存放最长重合前后缀长度+1
【第二位往后(k!=0)】如果Pj ≠ Pnext[j],那么next[j+1]可能的次大值为next[ next[ j ] ]+1,以此类推知道值为1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27/*
生成T串的next[]数组:
1. 定义子串索引变量i=1 (T(0)存放串长度),最长前缀表索引变量k=0
2. next[1]数组首位为0
3. 判断:当前位置i小于串长度
1. 判断:前缀k==0 或 前缀最后一位数据==最长前缀k数据
a.++i,++k
b.记录第i位最长前缀k
2. 数据不一样,回溯前一位匹配的k值(当前最长前缀不匹配,回朔至次长前缀接着比较)
*/
void get_next(String T,int *next)
{
int i=1,k=0; //1
next[1]=0; //2
while(i<T[0]) //3
{
if(k==0 || T[i]=T[k]) //3.1
{
++i;
++k; //3.1.a
next[i]=k; //3.1.b
}
else{
k=next[k]; //3.2
}
}
}经验总结:前后缀模式串n个字符相等,k值为n+1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38/*
1. 定义索引主串的i,索引子串j
2. 生成预加载next数组长度;
3. 调用函数get_next计算传入子串T的next数组
4.判断子主串小于等于串长
1. 判断j==0 或 主串值==子串值
a.索引指针++
2. 子串索引j按照next数组退回到合适位置
4. 判断子串索引结束
a.遍历完成,返回位置
5. 不匹配,返回0
*/
int Index_KMP(String S,String T,int pos)
{
int i=pos;
int j=1;
int next[MAXSIZE]; //2
get_next(T,next); //3
while(i<=S[0] && j<=T[0]) //4
{
if(j==0 || S[i]==T[j]) //4.1
{
i++;
j++;
}
else
{
j=next[j]; //4.2
}
}
if(j>T[0]) //5
{
return i-T[0];
}
else{
return 0; //6
}
}
改进版KMP模式匹配算法(改良next[]数组)
- 计算next数组值时,如果a位字符与它next值指向的b位字符相等,则该a位的nextval就指向b位的nextval
- 如果不等,则该a位的nextval值就是它自己的next的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36/*
1.定义子串索引变量i=1 (T(0)存放串长度),最长前缀表索引变量k=0
2.nextval[1]数组首位为0
3.判断:子串当前索引i小于子串长度
1.判断:前缀长度k==0 或 当前i位置数据==最长前缀k数据
a.i++,k++
b.判断:当前i位置数据 != 最长前缀k位置的数据
当前k为此i位置的最长前缀,赋值给nextval数组
c.当前位置数据 == 前缀k位置数据
k位置的nextval值赋值给当前i位置nextval值
2.数据不相等,或k != 0,(k值回朔)次长前缀值nextval[k]赋值给当前索引k
*/
void get_nextval(String T,int *nextval)
{
int i=1;
int k=0; //1
nextval[1]=0; //2
while(i<T[0])
{
if(k==0 || T[i]==T[k])
{
++i;
++k; //3.1.a
if(T[i]!=T[k])
{
nextval[i]=k; //3.1.b
}
else{
nextval[i]=nextval[k]; //3.1.c
}
}
else{
k=nextval[k]; //3.2
}
}
}
查看评论