数据结构第四章:串

一、什么是"串"?

在数据结构中,串(String)是由零个或多个字符组成的有限序列

比如 "hello""a+b*c" 都是串。

  • 主串:被查找的长字符串(如文本)
  • 子串:主串中任意连续的一段(如关键词)
  • 空串:长度为 0 的串(不是空格!)

小考点 :空格 ' ' 是一个有效字符,计入串长!


二、串的存储结构(选择题常考)

表格

存储方式 特点 优缺点
定长顺序存储 用固定长度数组存储 简单,但空间浪费或溢出
堆分配存储 动态申请内存(C 语言 malloc 长度可变,最常用
块链存储 每块存若干字符,用链表连接 适合超长串,但指针开销大

📌 考研重点:堆分配是实际应用中最灵活的方式;定长适合已知长度的场景。

🔹 定长顺序存储结构:

复制代码
#define MaxLen 255 //预定义最大串长为255
typedef struct { 
    char ch[MaxLen]; //每个分量存储一个字符
    int length; //串的实际长度
} SString;

🔹 堆分配存储结构:

cpp 复制代码
typedef struct {
    char *ch; //按串长分配存储区,ch 指向串的基地址
    int length; //串的长度
} HString;

🔹 块链存储结构:

最后一个结点占不满时通常用"#"补上

三、模式匹配:暴力法 vs KMP(重中之重!)

1. 暴力匹配法(Brute Force)

cpp 复制代码
#define MaxLen 255 //预定义最大串长为255
typedef struct { 
    char ch[MaxLen]; //每个分量存储一个字符
    int length; //串的实际长度
} SString;

// 简单的模式匹配算法 S: 主串 T: 子串 返回:字串在主串中的首字符位置下标
int index(SString S, SString T) {
    // 字符串S下标
    int i = 1;
    // 查找的子串T
    int 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;
    }
}

2. KMP 算法:不回退的高效匹配

KMP 的核心思想就一句:

"利用已经匹配的信息,让模式串尽可能多地'滑动',避免主串指针回退。"

🔑 关键:next 数组

next[i] 表示:模式串前 i 个字符中,最长相等真前后缀的长度

  • 真前后缀:不能是整个串本身,也不能是空串。
  • 作用 :当第 i 位失配时,模式串跳到 next[i] 位置继续匹配。
✅ 手动构造 next 数组(以 "ababaa" 为例)

表格

i(从1开始) 1 2 3 4 5 6
字符 a b a b a a
nexti 0 0 1 2 3 1

构造逻辑简述

  • i=1:单个字符,无真前后缀 → 0
  • i=3"aba",前缀 "a" = 后缀 "a" → 长度 1
  • i=6"ababaa",最长相等真前后缀是 "a" → 长度 1

📌 考研必考:给一个模式串,手算 next 数组!

3. KMP 匹配过程(主串指针永不回退!)

这里使用新的next\[\]数组,基于标准的next数组做了变更

1.标准next数组:

  1. PM表向右移移动一位
  1. next数组整体+1
cpp 复制代码
#define MaxLen 255 //预定义最大串长为255
typedef struct { 
    char ch[MaxLen]; //每个分量存储一个字符
    int length; //串的实际长度
} SString; 

// KMP匹配 S: 主串 T: 子串 next[]: PM表
int index_KMP(SString S, SString T, int next[]) {
    // 主串S的下标
    int i = 1;
    // 子串T的下标
    int 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)O(m+n) ------ 线性时间,完胜暴力法!

  1. KMP算法的进一步优化
  • T.ch[j] == T.ch[next[j]],则 nextval[j] = nextval[next[j]]
  • 否则,nextval[j] = next[j]

📌 注意:使用 nextval[next[j]](而非 next[next[j]])是为了递归跳过所有相同字符 ,确保最终指向一个不同字符的位置

相关推荐
.千余17 小时前
【C++】C++ set 与 multiset 完全指南:关联式容器入门
开发语言·c++·笔记·学习·其他
c++之路20 小时前
CMake 系列教程(二):基础命令详解
开发语言·c++
阿维的博客日记1 天前
Hippo4j 线程池监控平台部署手册
java·spring boot·后端
南境十里·墨染春水1 天前
C++ 工厂模式:从入门到进阶,彻底掌握对象创建的艺术
开发语言·c++·算法
C+++Python1 天前
详细介绍一下Java泛型的通配符
java·windows·python
JosieBook1 天前
【数据库】时序预测能力的分级进化:TimechoAI如何让每一类用户都能精准预见未来
java·开发语言·数据库
加号31 天前
【C#】 文件与目录管理:创建、删除操作的技术解析
开发语言·c#
diving deep1 天前
脚本速览-python
开发语言·python
一生了无挂1 天前
Java处理JSON技巧教学(从基础到高阶实战全覆盖)
java·开发语言·json
李白的天不白1 天前
使用 SmartAdmin 进行前后端开发
java·前端