这是本人根据王道考研数据结构课程整理的笔记,希望对您有帮助。
串的定义
串,即字符串(String)是由零个或多个字符组成的有限序列。一般记为: S = ′ a 1 a 2 … a n ′ ( n ≥ 0 ) S='a_1a_2\dots a_n'(n\ge0) S=′a1a2…an′(n≥0)(C、Java用双引号,Python用单引号)。
串和线性表
串是一种特殊的线性表,数据元素之间呈线性关系
串的数据对象限定为字符集(如中文字符、英文字符、数字字符、标点字符等)
串的基本操作,如增删改查等通常以字串为操作对象
串的基本操作
假设有串T=""
,S="iPhone 11 Pro Max"
,W="Pro"
StrAssign(&T, chars)
:赋值操作。把串T赋值为chars。
StrCopy(&T, S)
:复制操作。由串S复制得到串T。
StrEmpty(S)
:判空操作。若S为空串,则返回true,否则返回false。
StrLength(S)
:求串长。返回串S的元素个数。
ClearString(&S)
:清空操作。将S清为空串。
DestroyString(&S)
:销毁串。将串S销毁(回收存储空间)。
Concat(&T, S1, S2)
:串联接。用T返回由S1和S2联接而成的新串。
SubString(&Sub, S, pos, len)
:求子串。用Sub返回串S的第pos个字符起长度为len的字串。
Index(S, T)
:定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。
StrCompare(S, T)
:比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S=T,则返回值=0;若S
串的顺序存储
//【静态数组】定长顺序存储
#define MAXLEN 255
typedef struct
{
char ch[MAXLEN];
int length;
}SString;
//【动态数组】堆分配存储
typedef struct
{
char *ch;
int length;
}HString;
HString S;
S.ch = (char *)malloc(MAXLEN * sizeof(char));
S.length = 0;
SubString(&Sub, S, pos, len)
:求子串。用Sub返回串S的第pos个字符起长度为len的字串。
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;
}
串的模式匹配:再主串中找到与模式串相同的子串,并返回其所在位置。 朴素模式匹配算法的缺点:当某些子串与模式串能部分匹配时,主串的扫描指针 串的前缀:包含第一个字符,且不包含最后一个字符的子串合集。(如果是单字符就不是前缀) 串的后缀:包含最后一个字符,且不包含最后一个字符的子串合集。(如果是单字符就不是前缀) 模式串取前缀(从前往后取),主串取后缀(从后往前取),看看前缀和后缀是否相等。 注: 代码解释: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ly5izgEe-1650375154062)(数据结构.assets\051048038058339.png)] 假设第 由上面的结论可以衍生出: 现在考虑第 KMP算法 在匹配StrCompare(S, T)
:比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S=T,则返回值=0;若Sint 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;
}
Index(S, T)
:定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。int Index(SString S, SString T)
{
int i = 1, n = StrLength(S), m = StrLength(T);
SString sub; //用于暂存子串
while(i <= n-m+1)
{
SubString(sub, S, i, m);
if(StrCompare(sub, T) != 0)
++i;
else return i; //返回子串再主串中的位置
}
return 0; //S中不存在于T相等的子串
}
4.2 串的模式匹配
4.2.1 串的朴素模式匹配算法
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;
j = 1;
}
}
if(j > T.length)
return k;
else
return 0;
}
4.2.2 KMP算法
i
经常回溯,导致时间开销增加。
最长相等前后缀的长度+1
即为next[j]
的值。
next[1] = 0
next[2] = 1
void get_next(SString T, int next[])
{
int j = 1; k = 0;
next[1] = 0;
while(j < T.length)
{
if(k == 0 || T.ch[j] == T.ch[k])
{
++j;
++k;
//若pj = pk,则next[j+1] = next[j] + 1
next[j] = k;
}
else
//否则令k = next[k],循环继续
k = next[k];
}
}
j
位以及第j
位之前next数组的都填完了,那么就会有如下已知条件:
next[j] == k
→ A1子串 == A2子串
next[k] == 绿色色块所在的索引
→ B1子串 == B2子串
next[绿色色块所在的索引] == 黄色色块所在的索引
→ C1子串 == C2子串
A1子串 == A2子串
+ B1子串 == B2子串
→ B1子串 == B2子串 == B3子串
B1子串 == B2子串 == B3子串
+ C1子串 == C2子串
→ C1子串 == C2子串 == C3子串 == C4子串
j+1
位的情况:
str[j] == str[k]
,那么很明显next[j+1] == k+1
(比较A1子串和A2子串延长后是否仍然一致)str[j] != str[k]
,那么令k == next[k]
(比较B1子串和B3子串延长后是否仍然一致),继续上一步操作直至str[j] == str[k]
。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])
{
++i;
++j; //继续比较后继字符
}
else
{
j = next[j]; //模式串向右移动
}
}
if(j > T.length)
return i - T.length; //匹配成功
else
return 0;
}
j=4
时,匹配失败会跳到j=1
,但是此时j=1
对应的模式串也是g
,这一次的对比是毫无意义的。nextval
数组的求法://【先算出next数组】
//【令nextval[1] = 0】
for(int j = 2; j<=T.length; j++)
{
if(T.ch[next[j]] == T.ch[j])
nextval[j] = nextval[next[j]];
else
nextval[j] = next[j];
}