关于数组及相关特殊矩阵的压缩存储内容 见书本(书本记录更方便)。
一、串的定义
字符串 简称 串,计算机上非数值处理的对象基本上都是字符数据
串:由零个或多个字符串组成
长度:串中字符个数n
空串:n=0
子串:子序列
主串:包含子序列的串
串中位置:某个字符串在串中的序号
子串在主串中的位置:子串的第一个字符串在主串中的位置
空格串:一个或多个空格组成的串(空格串不是空串,其长度为串中空格字符的个数)
A=‘China Beijing’,B=‘Beijing’,长度分别为:13,7。B是A的子串,B在A中的位置是7。
串与线性表 相似
区别:
数据对象:串—字符集
基本操作对象:串—子串;线性表—单个元素
二、串的存储结构
1.存储结构
顺序存储结构
//------串的定长顺序存储结构
#define MAXLEN 255 //串的最大长度
typedef struct {
char ch[MAXLEN + 1]; //存储串的一维数组(这里顺序存储的字符串是从下标为1的数组分量开始存储的,下标0闲置不用)
int length; //串的当前长度
}SString;
堆分配存储表示
//------堆分配存储表示
typedef struct {
char* ch; //按串长分配存储区,ch指向串的基地址
int length; //串长度
}HString;
链式存储
//------串的链式存储
#define CHUNKSIZE 80 //块的大小
typedef struct Chunk {
char ch[CHUNKSIZE];
struct Chunk* next;
}Chunk;
typedef struct {
Chunk* head, * tail; //串的头指针,尾指针
int length; //串的当前长度
}LString;
2.暴力算法(BF算法)
指针后退重新开始匹配:
i = i - j + 2;
j = 1;
匹配子串在主串位置:
i - T.length;
举例:
//暴力匹配算法(BF算法)
int Index(SString S, SString T) {
int i = 1, 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;
else
return 0;
}
时间复杂度O(mn),n:主串长度;m:子串长度。
3.KMP算法(改进的模式匹配算法)
1、前缀:除最后一个字符外,字符串的所有头部子串
2、后缀:除第一个字符外,字符串的所有尾部子串
3、部分匹配值:前缀后缀的最长相等前后缀长度
如:ababa 前缀:a ab aba abab;后缀:a ba aba baba,最长相等前后缀长度3。
同理:a—0,ab—0,aba—1,abab—2,ababa—3
所以字符串‘ababa’的部分匹配值为00123
4、部分匹配值(Partial Match,PM)表
5、子串需要向后移动个数
移动位数=已匹配的字符数-对应的部分匹配值
6、算法改进:
已知:右移位数=已匹配的字符数-对应的部分匹配值
写成:Move = ( j - 1 ) - PM [ j - 1 ]
使用部分匹配时,每匹配失败就要找前一个元素的部分匹配值,这样使用不方便,所以将PM表右移一位,即:
(1)第一个元素 右移后空缺用-1表示,若是第一个元素匹配失败,直接加一,即将子串相对右移,而不需要计算子串移动的位数
(2)最后一个元素 右移后溢出,因为在原来子串中,最后一个元素的部分匹配值是给下一个元素使用,而最后一个元素没有下一个元素,可舍去。
上式写成:Move=(j-1)-next[j]
相当于子串的 j 回到:j=j-Move=j-((j-1)-next[j])=next[j]+1
即next数组更新:
7、next函数公式:
8、思绪理解
(1)
(2)例题中的
9、子串next
//子串next
void get_next(SString T, int next[]) {
int i = 1, j = 0;
next[1] = 0;
while (i < T.length) {
if (j == 0 || T.ch[i] == T.ch[j]) {
++i;
++j; //1
next[i] = j; //2,若pi=pj,则next[j+1]=next[j]+1
}
else
{
j = next[j];
}
}
}
10、KMP算法
//KMP
int Index_KMP(SString S, SString T, int next[]) {
int i = 1, j = 1;
while (i < S.length && j < T.length) {
if (j == 0 || S.ch[i] == T.ch[j]) { //0是当比较第一个字符时
++i;
++j;
}
else
j = next[j];
}
if (j > T.length)
return i - T.length;
else
return 0;
}
时间复杂度O(m+n),n:主串长度;m:子串长度。
4.MKP算法的进一步优化
修正一些相等的字符,避免重复
一路和next[j]比较
若不等,取原来的next[j],即不变
若相等,继续向前比较,直到找到不等,取不等位的next,或直到j=0。
例:
子串nextval代码:
//子串nextval
void get_nextval(SString T, int nextval[]) {
int i = 1, j = 0;
nextval[1] = 0;
while (i < T.length) {
if (j == 0 || T.ch[i] == T.ch[j]) {
++i;
++j;
if (T.ch[i] != T.ch[j])
nextval[i] = j;
else
nextval[i] = nextval[j];
}
else
{
j = nextval[j];
}
}
}
三、错题
1、已知字符串S为’ abaabaabacacaabaabcc’,模式串t为’abaabc’. 采用KMP算法进行匹配,第一次出现“失配”(s[i]≠t[j]) 时,i=j=5,则下次开始匹配时,i和j的值分别是( )。
A. i=1,j=0
B. i=5,j=0
C. i=5,j=2
D. i=6,j=2
2、设主串T=’ abaabaabcabaabc’,模式串S= ‘abaabc’,采用KMP算法进行模式匹配,到匹配成功时为止,在匹配过程中进行的单个字符间的比较次数是 ( )。
A.9
B.10
C.12
D.15