C++数据结构考研chapter4串

本文详细介绍了字符串的存储结构,包括顺序存储(静态和动态)和链式存储,重点讲解了朴素模式匹配、KMP算法及其优化,以及如何计算nextarr和nextValarr。探讨了算法的时间复杂度和应用场景。
摘要由CSDN通过智能技术生成

本章节主要掌握字符串的模式匹配,主要包括朴素、KMP、KMP再优化,nextarr、nextValarr的手算

目录

一、定义

二、存储结构

1.顺序存储

(1)静态arr

(2)动态arr(堆存储)

2.链式存储

(1)单链表实现

3.王道教材中设置

4.基本操作(使用王道教材中的模式展示)

(1)定义(在上述二中详细展示)

(2)初始化

(3)创建串

(4)赋值串

(5)复制串

(6)求子串

(7)串的比较

(8)求串长

(9)求子串在主串中的位置

(10)将串清空

(11)判空

(12)拼接串

(13)打印

三、字符串的模式匹配(重点,考点)

1.术语

(1)主串

(2)子串

(3)模式串

(4)前缀

(5)后缀

(6)部分匹配值

2.算法

(1)朴素模式匹配算法

1)思路:暴力破解,挨个遍历。

2)代码实现

3)Tn分析

(2)KMP算法

1)思路

2)手算

3)eg

4)代码实现

5)Tn分析

(3)KMP再优化

1)思路

2)手算

3)代码实现


一、定义

即字符串

二、存储结构

1.顺序存储

(1)静态arr

typedef struct {
    ELemType ch[MaxLen];
    int length;
}SString;

(2)动态arr(堆存储)

typedef struct {
    ELemType *ch;
    int length;
}HString;

2.链式存储

(1)单链表实现

#define SIZE 20
typedef struct LSNode{
    ELemType data[SIZE];//多存几个ele
    struct LinkString *next;
}LSNode;

3.王道教材中设置

舍弃第一个元素,再使用length变量

优点:arr中ele的index==ele的pos

4.基本操作(使用王道教材中的模式展示)

(1)定义(在上述二中详细展示)

typedef char ELemType;
#define MaxLen 255

typedef struct {
    ELemType ch[MaxLen];
    int length;
}SString;

(2)初始化

void InitSString(SString &SS){
    SS.length = 0;
}

(3)创建串

第一个pos的ele为null

bool CreateSString(SString &SS,ELemType* arr){
    int len = strlen(arr);
    for (int i = 0; i < len; ++i) {
        SS.ch[i+1]=arr[i];
    }
    SS.length=len+1;//此处+1是因为第一个下标ele为空
    return true;
}

attn:在使用函数时,需要传入char*类型的ele,不建议直接传入字符串常量,因为字符串常量在编译的时候会转为char*类型,IDE会waring

CreateSString(T,(ELemType *)"abaabc");、//recommend

CreateSString(T,"abaabc");

(4)赋值串

此处使用的是单个字符依次复制,也可使用其他方式,但不建议使用<string>or<string.h>库,因为我们正在练习这一部分
eg:①<string.h>的strcpy()②从键盘中获取getchar()or gets()再用%s赋值
//赋值 把串SS赋值为arr
bool StringAssign(SString SS, SString &chars) {
    if (SS.length == 0) return false;
    chars = SS;
    return true;
}

(5)复制串

//复制 将ST每个单个字符复制给chars
bool StrCopy(SString ST, SString &chars) {
    if (ST.length == 0) return false;
    for (int i = 1; i < ST.length; ++i) {
        chars.ch[i] = ST.ch[i];
    }
    chars.length = ST.length;
    return true;
}

(6)求子串

//求子串 返回从pos位置开始长度为len的子串
bool SubString(SString &arr, SString SS, int pos, int len) {
    if (pos + len > SS.length) return false;
    for (int i = pos; i < pos + len; ++i) {
        arr.ch[i - pos + 1] = SS.ch[pos];
    }
    arr.length = len;
    return true;
}

(7)串的比较

application:英语单词书(顺序版)

//比较 S>T,返回1 相等0 小于-1
int StrCompare(SString SS, SString T) {
    int i = 1;
    while (i < SS.length && i < T.length) {//比较前i位
        if (SS.ch[i] == T.ch[i]) i++;
        else if (SS.ch[i] > T.ch[i]) return 1;
        else if (SS.ch[i] < T.ch[i]) return -1;
    }
    //有一个到头了,表示前i位相同
    if (SS.length == T.length) return 0;
    else if (SS.length < T.length) return -1;
    else  return 1;
}

(8)求串长

//求ele不为空的长度
int StrLength(SString SS) {
    return SS.length - 1;
}

(9)求子串在主串中的位置

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-1) {
        return i - j + 1;
    } else return 0;//未找到
}

(10)将串清空

void ClearString(SString &S){
    while(S.length!= 0){
        S.ch[S.length--] = NULL;
    }
}

(11)判空

bool StrEmpty(SString SS){
    return SS.length == 0;
}

(12)拼接串

两次遍历
bool Concat(SString &T, SString S1, SString S2) {
    if (MaxLen < S1.length + S2.length) return false;
    for (int i = 1; i < S1.length; ++i) {//复制s1
            T.ch[i] = S1.ch[i];
    }
    for (int i = 1; i < S2.length; ++i) {//复制s2
            T.ch[i + S1.length] = S2.ch[i];
    }
    T.length = S1.length+S2.length;
    return true;
}

但是我连接的两个之间有一个空元素,未解决,∵不是考点,so没有浪费时间

(13)打印

//打印
void print(SString SS){
    for (int i = 1; i < SS.length; ++i) {
        printf("%c",SS.ch[i]);
    }
    printf("\n");
}

三、字符串的模式匹配(重点,考点)

1.术语

(1)主串

已给的一长串字符串

(2)子串

从主串中截取的字符串

(3)模式串

用于在主串中寻找的子串

(4)前缀

不包括第一个元素,能将主串拆解的所有的子串

(5)后缀

不包括最后一个ele,能将主串拆解的所有的子串

(6)部分匹配值

前缀和后缀中所有相同ele的个数

2.算法

(1)朴素模式匹配算法

1)思路:暴力破解,挨个遍历。

i从主串第一个ele开始,j从匹配串第一个ele开始,依次比对,当失配后i从主串第二个ele开始,j从匹配串第一个ele开始

2)代码实现
//定位操作 找到主串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-1){
        return i - j + 1;
    }else return 0;//未找到
}
3)Tn分析

设主串长度为n,匹配串长度为m

最多要进行n-m+1次遍历,而每次遍历匹配串的长度m

Tn = O((n-m+1)m) = O(nm-m^2-m)

因为 n>> m => Tn = O(nm)

(2)KMP算法

优化朴素

1)思路

在朴素中,失配的ele前面的说明是已经匹配上的,so我们拿匹配串失配ele之前的ele,依次与刚才匹配子串的第二个ele开始依次匹配,从而可以提高效率

2)手算

step1:先根据模式串进行nextarr的计算

step2:设置ij遍历主串和模式串,根据nextarr进行跳转

3)eg

模式串        a        b        a        a        b        c

nextarr        0        1        1        2        2        3

4)代码实现

最后此处j>T.length - 1是因为我把空元素也算在了长度内。

if 不算空元素,最后判断是否找到的条件为j>T.length

int KMP_Index(SString SS,SString T,int next[]){
    //SS是主串,T是模式串,next是nextarr
   int i =1, j =1;
    while(i<SS.length && j <T.length){
        if(j==0||SS.ch[i] == T.ch[j] ){
            i++,j++;
        } else{
            j = next[j];
        }
    }
    if(j>T.length - 1) return i-j+1;
    else return  0 ;
}
5)Tn分析

主串i不会往前走,j进行跳转,soT(n)max = O(n+m)

求nextarrTn=O(m) 最多移动匹配串长度,即m

模式匹配过程 Tn = O(n)

(3)KMP再优化

1)思路

假设模式串为abaabc,if第3个字符(a)失配,则需要跳到第1个字符(a)并在之后继续配比,但因为这个失配的字符是a,so跳到第一个字符a一定失配,so直接跳到第一个字符的nextarr即可,则新生成的arr称作nextval

结论:if失配的字符跳到的位置上的字符依然还是失配的字符,则直接让其nextval = 该位置上的字符的nextval 

2)手算

模式串        a        b        a        a        b        c

nextarr        0        1        1        2        2        3

nextval        0        1        0        2        1        3

3)代码实现
int KMP_better(SString SS, SString T, int nextval[]) {
//SS 主串 T 匹配串 nextval是nextvalarr
    int i = 1, j = 1;
    while (i < SS.length && j < T.length) {
        if (j==0||SS.ch[i] == T.ch[j]) {
            i++, j++;
        } else {
            j = nextval[j];
        }
    }
    if (j > T.length-1) return i - j + 1;
    else return 0;
}
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blue_blooded

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值