串、串长、空串、空格串、子串、主串、字符在主串中的位置、子串在主串中的位置
一、串的基本操作
存储结构为:定长顺序存储
1.1 赋值操作
StrAssign(&T, chars)
: 串T赋值为chars
SString StrAssign(SString &T, char *chars){
int i = 0;
while(chars[i] != '\0'){
T.ch[i] = chars[i];
i++;
}
T.length = i;
return T;
}
1.2 复制操作
StrCopy(&T, S)
: 串S复制得到串T
SString StrCopy(SString &T, SString S){
for(int i=0; i<S.length; i++)
T.ch[i] = S.ch[i];
T.length = S.length;
return T;
}
1.3 判空操作
StrEmpty(S)
: 串S为空,返回TRUE,否则返回FALSE
bool StrEmpty(SString S){
if(S.length == 0)
return true;
else
return false;
}
1.4 求串长
StrLength(S)
: 返回串S的元素个数
int StrLength(SString S){
return S.length;
}
1.5 清空操作
ClearString(&S)
: 串S清为空串
bool ClearString(SString &S){
S.length = 0;
return true;
}
1.6 销毁串
DestroyString(&S)
: 销毁串S,即回收存储空间
1.7 串连接
Concat(&S, T)
: S返回由串S和串T连接成的新串
bool Concat(SString &S, SString T){
// 插入后总串长 <= MAXLEN
if(S.length + T.length <= MAXLEN){
for(int i=S.length; i<S.length+T.length; i++)
S.ch[i] = T.ch[i-S.length];
S.length = S.length + T.length;
return true;
}else{
return false;
}
}
1.8 求子串 ⭐
SubString(&Sub, S, pos, len)
: Sub返回串S的第pos个字符起长度为len的子串
// 使用定长顺序存储
bool SubString(SString &Sub, SString S, int pos, int len){
// 子串越界
if(pos<0 || 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;
}
1.9 串比较操作 ⭐
StrCompare(S, T)
: 若S>T,则返回值 > 0;若S=T,则返回值 = 0;若S<T,则返回值 < 0
int StrCompare(SString S, SString T){
for(int i=0; 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;
}
1.10 定位操作 ⭐
Index(S, T)
: 若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0
int Index(SString S, SString T){
int i=0, n=StrLength(S), m=StrLength(T);
SString sub;
while(i <= n-m){
SubString(sub,S,i,m); // 获取S中的长度为m的子串
if(StrCompare(sub,T) != 0) // 比较子串与串T
i++;
else
return i; // 找到则返回子串在主串S中的位置
}
return -1; // 主串S中不存在与串T相等的子串
}
1.11 串插入操作
StrInsert(S, pos, T)
: 在串S中第pos位前插入串T
int StrInsert(SString S, int pos, SString T){
if(pos<0 || pos > S.length) // 插入位置不合法
return 0;
if(S.length + T,length <= MAXLEN)
}
1.12 串删除操作
StrDelete(&S, pos, len)
: 删除串S中自pos位起的len个字符
bool StrDelete(SString &S, int pos, int len){
if(pos<0 || pos>S.length-len) // 删除位置不合法
return false;
for(int i=pos+len; i<S.length; i++)
S.ch[i-len] = S.ch[i];
S.length -= len;
return true;
}
1.13 输出串
void PrintString(SString S){
for(int i=0; i<S.length; i++){
cout<<S.ch[i];
}
printf("\n");
}
二、串的存储结构
2.1 定长顺序存储(静态数组)
#define MAXLEN 255 // 预定义最大串长为255
typedef struct{
char ch[MAXLEN]; // 每个分量存储一个字符
int length; // 串的实际长度
}SString;
📌 tips:
王道书中采用 静态数组存储结构
ch[0] 废弃不用,ch[1] 开始,最后变量length存储长度
2.2 堆分配存储(动态数组)
typedef struct{
char *ch; // 按串长分配存储区,ch指向串的基地址
int length; // 串的实际长度
}HString;
HString S;
S.ch = (char *) malloc(MAXLEN * sizeof(char)); // 用完需手动free
S.length = 0;
2.3 链式存储
typedef struct StringNode{
char ch[4]; // 每个结点可存多个字符
struct StringNode *next;
}StringNode, *String;
三、字符串的模式匹配
3.1 朴素模式匹配算法
主串长度为n,模式串长度为m
朴素模式匹配算法:将主串中 所有长度为m的子串 依次与模式串对比,直到找到一个完全匹配的子串,或所有子串都不匹配为止。(最多有 n-m+1 个子串) —> 即 Index(S, T)
定位操作
// 另一方法:直接利用下标
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((n-m+1)m) = O(nm) (一般n>>m)
3.2 KMP算法
KMP算法:先预处理:根据模式串T,求出next数组;—> 利用next数组进行匹配(主串指针不回溯)
next数组只和短短的模式串有关,和长长的主串无关(掌握手算即可)
next数组作用:当模式串的第j个字符失配时,从模式串的第 next[j] 的继续往后匹配;
对任何模式串,其**next[1] = 0; next[2] = 1;
** 例:模式串 google
,其next数组如下:
next数组 | next[0] | next[1] | next[2] | next[3] | next[4] | next[5] | next[6] |
---|---|---|---|---|---|---|---|
j | 0 | 1 | 1 | 1 | 2 | 1 |
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; // 没找到
}
最坏时间复杂度 = O(m + n);
求next数组时间复杂度O(m); 模式匹配过程最坏时间复杂度O(n)
3.2.1 next数组的优化
序号j | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
模式串 | a | b | a | b | a | a |
next[j] | 0 | 1 | 1 | 2 | 3 | 4 |
nextval[j] | 0 | 1 | 0 | 1 | 0 | 4 |
// 先求next数组,再由next数组求nextval数组
nextval[1] = 0;
for(int j=2; j<=T.length; j++){
// 模式串中第j个 <--对应--> next[j]所指模式串中字符
if(T.ch[j] == T.ch[next[j]]) // 如j=4,b <--> next[4]=2,j=2,b 字符相等
nextval[j] = nextval[next[j]];
else
nextval[j] = next[j];
}