数据结构第3章 串

news2024/11/23 0:13:53

名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪)
本篇笔记整理:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)

目录

    • 0、思维导图
    • 1、基本概念
      • 1)主串
      • 2)子串
      • 3)空串
      • 4)串长
    • 2、存储结构
      • 1)顺序存储
      • 2)链式存储
    • 3、模式匹配算法
      • 1)简单模式匹配
      • 2)KMP算法
      • 3)KMP算法改进

0、思维导图

在这里插入图片描述

1、基本概念

1)主串

  • 包含子串的串,即一个完整的字符串

    例如:“数据结构"是一个主串,它包含了"数”、“据”、“结”、"构"等子串。

2)子串

  • 串中任意个连续的字符组成的子序列,即一个字符串的一部分

    例如:“数据结构"的子串有"数”、“据”、“结”、“构”、“数据”、"结构"等。
    “abcd"的子串有"a”、“b”、“c”、"d"等。

3)空串

  • 无任何字符组成的串

    空串的长度为零,用""表示。例如,""是一个空串

4)串长

  • 串中字符的个数

    为一个非负整数,用n表示。例如,"abcd"的串长为4。

2、存储结构

1)顺序存储

顺序存储结构使用一段连续的存储单元一次存放串中的字符。这种结构类似于数组的存储方式,每个字符占用一个存储单元。
在这里插入图片描述

在串的顺序存储结构中,有两种常见的实现方式:定长顺序存储和块分配存储。这两种方式都是在连续的存储单元中存放字符串,但它们在管理这些存储单元的方式上有所不同。

1️⃣定长顺序存储

typedef struct SString
{
	char ch[N];
	int length;
}SString;

在定长顺序存储中,每个字符串都被分配一个固定大小的存储区域。这个固定大小通常是根据应用场景中字符串的最大长度来设定的。

  • 优点

    • 存取简单快捷,可以直接通过索引访问任何一个字符
    • 存储结构固定,便于管理。
  • 缺点

    • 不够灵活。如果分配的存储空间大于实际需要,会造成存储空间的浪费;如果小于实际需要,则无法存放更长的字符串。
    • 调整字符串长度时(如拼接、截断操作),可能需要复制整个字符串到新的存储区域,效率较低

2️⃣块分配存储

typedef struct Hstring
{
	char *ch;
	int length;
}Hstring;

块分配存储(也称为堆分配存储)中,字符串被存储在动态分配的内存块中。每个字符串可以根据需要分配合适大小的存储空间。

  • 优点

    • 灵活高效地利用内存。仅根据字符串的实际长度分配存储空间,避免了空间浪费
    • 易于处理长度变化较大的字符串操作,如拼接、修改等。
  • 缺点

    • 存储管理相对复杂。需要动态分配和释放内存,可能导致内存碎片。
    • 相较于定长存储,访问字符时可能略微慢一些,因为可能涉及到指针跳转。

3️⃣区别

  • 定长顺序存储适用于字符串长度固定或变化不大的应用场景,如固定长度的记录处理。
  • 块分配存储则适用于字符串长度变化较大的场景,特别是那些需要频繁进行字符串拼接、截断等操作的应用。

2)链式存储

链式存储结构通过一系列的节点来存储串中的字符,每个节点包含一个字符和指向下一个节点的指针。这种结构类似于链表。

3、模式匹配算法

1)简单模式匹配

简单模式匹配算法(也称为朴素模式匹配算法或暴力模式匹配算法)是一种最基本的字符串搜索方法,它尝试在主字符串(通常表示为 S S S)中查找一个子串(称为模式串,表示为 P P P),并返回模式串在主字符串中的位置。

1️⃣算法步骤

  1. 初始化:令主字符串的位置索引为 i = 0 i=0 i=0,模式串的位置索引为 j = 0 j=0 j=0
  2. 匹配:比较主字符串的第 i i i 个字符和模式串的第 j j j 个字符。
    • 如果相等, i i i j j j 同时加一,继续比较下一个字符。
    • 如果不相等,将 i i i 回溯到上一次匹配开始的下一个字符位置,即 i = i − j + 1 i = i - j + 1 i=ij+1,并将 j j j 重置为 0 0 0,重新开始匹配。
  3. 重复:重复步骤2,直到模式串完全匹配,或主字符串的字符比较完毕。
  4. 返回结果:如果模式串 P P P 在主字符串 S S S 中完全匹配,返回模式串开始匹配的位置;否则,返回 − 1 -1 1 或其他表示不匹配的值。

2️⃣举例

假设我们有主字符串 S = "ABCDABCD" S = \text{"ABCDABCD"} S="ABCDABCD" 和模式串 P = "ABC" P = \text{"ABC"} P="ABC"

  • 第一次比较 S [ 0 … 2 ] S[0\ldots2] S[02] P [ 0 … 2 ] P[0\ldots2] P[02],匹配成功。
  • 如果我们寻找下一个匹配,当 S [ 1 … 3 ] S[1\ldots3] S[13] P [ 0 … 2 ] P[0\ldots2] P[02] 不匹配时, i i i 会回溯到 2 2 2(即 1 − 0 + 1 1 - 0 + 1 10+1), j j j 重置为 0 0 0,然后继续匹配。

2)KMP算法

1️⃣概念

KMP算法(Knuth-Morris-Pratt字符串搜索算法),是一种高效的字符串匹配算法。它在不匹配时,通过已匹配的部分信息避免从头开始搜索,从而提高匹配效率。KMP算法的核心构建一个部分匹配表(也称为"失败函数"或"前缀函数"),用以在字符串匹配过程中跳过不必要的比较。

部分匹配表的构建基于以下概念:

  • 前缀:对于字符串 S S S S S S 的前缀是从 S S S 的开头开始的任何子字符串。
  • 后缀:对于字符串 S S S S S S 的后缀是从 S S S 的结尾结束的任何子字符串。

部分匹配表为每个位置 i i i 计算一个值,该值表示字符串从头开始到位置 i i i 的子字符串中,有多长的相同前缀和后缀。这个值被用于当字符不匹配时,确定下一步匹配的起始位置。

2️⃣举例
例如,对于字符串 “ABCDABD”,其部分匹配表如下:

索引0123456
字符ABCDABD
0000120

在实际的字符串搜索过程中,KMP算法使用这个表来决定在不匹配时下一步应该跳过多少个字符。这样,算法避免了从主字符串的每个字符开始匹配,显著提高了搜索效率。

3️⃣补充(PM和next数组求解步骤)

①部分匹配值表(PM)

②next数组求解步骤

  • a.写出PM表

  • b.将PM表值整体右移一位,首位补-1

    • 若题目所给序列是0开始,则此时得到的序列就是next数组

    • 若是从1开始的,则需要整体+1

3)KMP算法改进

nextval数组求解步骤

  • a.nextval[1]=0

  • b.若当前位字符与next数组所对应的字符相比较

    • 如果相等则为nextval数组所对应的next的值

    • 即:nextval[j] = nextval[next[j]]

    • 如果不相等则为next数组所对应的值

    • 即:nextval[j]=next[]j

  • c.具体步骤如下:

    • 1.先将next[j]数组对应的模式串写出来,如果next[j] = 1,那么就写对应j = 1的模式串字符,其余同理

    • 2.写出next[j]数组对应的模式串后,将其与j的模式串字符相比较,如果两者不相等,则nextval[j] = next [j]

    • 3.如果两者相等,则nextval[j]的值等于next[j]的值对应j下的nextval的值,例如next[j] = 1,对应的j = 1,j=1下的nextval值等于0,则nextval[j] = nextval[1] = 0。(也就是nextval[j] = nextval[next[j]])
      在这里插入图片描述

C语言实现KMP算法求解next数组和nextval数组:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
using namespace std;

void computeLPSArray(char* pat, int M, int* lps) {
    int len = 0;
    lps[0] = 0;
    int i = 1;
    while (i < M) {
        if (pat[i] == pat[len]) {
            len++;
            lps[i] = len;
            i++;
        } else {
            if (len != 0) {
                len = lps[len - 1];
            } else {
                lps[i] = 0;
                i++;
            }
        }
    }
}

void computeNextValArray(char* pat, int M, int* lps, int* nextval) {
    nextval[0] = -1;
    for (int i = 1; i < M; i++) {
        int j = lps[i - 1];
        while (j >= 0 && pat[i] != pat[j]) {
            j = nextval[j];
        }
        nextval[i] = j + 1;
    }
}

void printArray(int* arr, int size, string name) {
    cout<<name<<":"<<endl;
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void KMPSearch(char* pat, char* txt) {
    int M = strlen(pat);
    int N = strlen(txt);
    int* lps = (int*)malloc(M * sizeof(int));
    int* nextval = (int*)malloc(M * sizeof(int));

    computeLPSArray(pat, M, lps);
    computeNextValArray(pat, M, lps, nextval);

    // 打印lps (next数组) 和 nextval数组
    printArray(lps, M, "Next");
    printArray(nextval, M, "NextVal");

    int i = 0;
    int j = 0;
    while (i < N) {
        if (pat[j] == txt[i]) {
            j++;
            i++;
        }
        if (j == M) {
            printf("模式匹配索引所在位置: %d \n", i - j);
            j = lps[j - 1];
        } else if (i < N && pat[j] != txt[i]) {
            if (j != 0) {
                j = nextval[j]; // 使用nextval优化跳转
            } else {
                i = i + 1;
            }
        }
    }
    free(lps);
    free(nextval);
}

int main() {
    char txt[] = "ABABDABACDABABCABAB";
    char pat[] = "ABABCABAB";
    KMPSearch(pat, txt);
    return 0;
}

在这里插入图片描述

上述内容笔记部分图片来源网络,侵删。
参考内容:
1.《王道数据结构》
2.计算next和nextVal值
3.数据结构电子讲义

Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder)
点赞加关注,收藏不迷路!本篇文章对你有帮助的话,还请多多点赞支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1457907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java+SpringBoot:农业疾病防治新选择

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

NBlog个人博客部署维护过程记录 -- 后端springboot + 前端vue

项目是fork的Naccl大佬NBlog项目&#xff0c;页面做的相当漂亮&#xff0c;所以选择了这个。可以参考2.3的效果图 惭愧&#xff0c;工作两年了也没个自己的博客系统&#xff0c;趁着过年时间&#xff0c;开始搭建一下. NBlog原项目的github链接&#xff1a;Naccl/NBlog: &#…

leetcode(动态规划)53.最大子数组和(C++详细解释)DAY12

文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 提示 2.解答思…

Sora一出 哪里又要裁员了?

上班前夕迎来大新闻&#xff0c;那就是Sora了&#xff0c;Sora是什么&#xff0c;有什么牛逼之处&#xff0c;怎么实现的&#xff0c;我们跟着官方文档透露出来的一点点信息&#xff0c;简单的捋一捋。 一、Sora是什么 官方给出的定义是&#xff1a;世界模拟器。这很明显有夸大…

数据结构:动态内存分配+内存分区+宏+结构体

一、作业 1.定义一个学生结构体&#xff0c;包含结构体成员&#xff1a;身高&#xff0c;姓名&#xff0c;成绩&#xff1b;定义一个结构体数组有7个成员&#xff0c;要求终端输入结构体成员的值&#xff0c;根据学生成绩&#xff0c;进行冒泡排序。 #include <stdio.h>…

Qt C++春晚刘谦魔术约瑟夫环问题的模拟程序

什么是约瑟夫环问题&#xff1f; 约瑟夫问题是个有名的问题&#xff1a;N个人围成一圈&#xff0c;从第一个开始报数&#xff0c;第M个将被杀掉&#xff0c;最后剩下一个&#xff0c;其余人都将被杀掉。例如N6&#xff0c;M5&#xff0c;被杀掉的顺序是&#xff1a;5&#xff…

如何利用Idea创建一个Servlet项目(新手向)

&#x1f495;"Echo"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;如何利用Idea创建一个Servlet项目(新手向) Servlet是tomcat的api,利用Servlet进行webapp开发很方便,本文将介绍如何通过Idea创建一个Servlet项目(一共分为七步,这可能是我们写过的…

备战蓝桥杯---动态规划(应用1)

话不多说&#xff0c;直接看题&#xff1a; 首先我们考虑暴力&#xff0c;用二维前缀和即可&#xff0c;复杂度为o(n^4). 其实&#xff0c;我们不妨枚举任意2行&#xff0c;枚举以这个为边界的最大矩阵。 我们把其中的每一列前缀和维护出来&#xff0c;相当于把一个矩阵压缩成…

观察者模式和发布订阅模式的区别

从下图中可以看出&#xff0c;观察者模式中观察者和目标直接进行交互&#xff0c;而发布订阅模式中统一由调度中心进行处理&#xff0c;订阅者和发布者互不干扰。这样一方面实现了解耦&#xff0c;还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息&#xff0c;但…

【Vue3】搭建Pinia环境及其基本使用

下载 npm i pinia引入并注册 App.vue import { createApp } from vue import { createPinia } from pinia import App from ./App.vue // 1. 引入 import { createPinia } from piniaconst app createApp(App) // 2. 创建 const pinia createPinia() // 3. 注册 app.use(p…

python----面向对象

这里写目录标题 面向对象思想类类的定义类名的定义类的构造函数的定义类的属性类的方法定义 继承语法关于构造函数问题 文件操作绝对路径相对路径pycharm获取绝对路径和相对路径文件读写读文件open&#xff08;&#xff09;read&#xff08;&#xff09;readline&#xff08;&a…

2021年CSP-J认证 CCF信息学奥赛中小学初级组 第一轮真题-单项选择题解析

2021年 中小学信息学奥赛CSP-J真题解析 1、以下不属于面向对象程序设计语言的是 A、c B、python C、java D、c 答案&#xff1a;D 考点分析&#xff1a;主要考查编程语言&#xff0c;ABC都是面向对象语言&#xff0c;D选项c语言是面向过程语言&#xff0c;答案D 2、以下奖…

202427读书笔记|《猫的自信:治愈系生活哲学绘本》——吸猫指南书,感受猫咪的柔软慵懒与治愈

202427读书笔记|《猫的自信&#xff1a;治愈系生活哲学绘本》——吸猫指南书&#xff0c;感受猫咪的柔软慵懒与治愈 《猫的自信&#xff1a;治愈系生活哲学绘本》作者林行瑞&#xff0c;治愈系小漫画绘本&#xff0c;10分钟可以读完的一本书&#xff0c;线条明媚&#xff0c;自…

SQL注入工具之SQLmap入门操作

了解SQLmap 基础操作 SQLmap是一款自动化的SQL注入工具&#xff0c;可以用于检测和利用SQL注入漏洞。 以下是SQLmap的入门操作步骤&#xff1a; 1.下载SQLmap&#xff1a;可以从官方网站&#xff08;https://sqlmap.org/&#xff09;下载最新版本的SQLmap。 2.打开终端&#…

CDP和Chrome

CDP和Chrome CDP和WebDriver Protocol WebDriver和 Chrome DevTools Protocol&#xff08;CDP&#xff09; 是用于自动化浏览器的两个主要协议&#xff0c;大多数的浏览器自动化工具都是基于上述其中之一来实现的。可以通过这两种形式来和浏览器交互&#xff0c;通过代码来控…

使用maven集成spring在测试的时候报出了如下的异常:version 60

使用maven集成spring在测试的时候报出了如下的异常&#xff1a; Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 60 解决&#xff1a;

MAC M1安装vmware和centos7虚拟机并配置静态ip

一、下载vmware和centos7镜像 1、VMWare Fusion 官网的下载地址是&#xff1a;下载地址 下载好之后注册需要秘钥&#xff0c;在官网注册后使用免费的个人秘钥 2、centos7 下载地址&#xff1a; https://biosyxh.cn:5001/sharing/pAlcCGNJf 二、虚拟机安装 直接将下…

比特币正在蚕食黄金

号外&#xff1a;教链内参2.19《内参&#xff1a;蹭热点、骗流量、割韭菜》 众所周知&#xff0c;自从美国SEC批准比特币现货ETF登陆美股市场之后&#xff0c;打开了美国金融市场泛滥的流动性向比特币流入的大门。只用了短短的30个交易日&#xff0c;比特币ETF就从零膨胀到了近…

基于RBAC的权限管理的理论实现和权限管理的实现

权限管理的理论 首先需要两个页面支持&#xff0c;分别是角色管理和员工管理&#xff0c;其中角色管理对应的是角色和权限的配合&#xff0c;员工管理则是将登录的员工账号和员工所处的角色进行对应&#xff0c;即通过新增角色这个概念&#xff0c;让权限和员工并不直接关联&a…

Ubuntu18.04有线连接后,无法设置ip地址以及显示网口设置

前提&#xff1a;首先测试过网线是完全没问题的 桌面端找不到设置网口 终端输入&#xff1a; ifconfig 没有找到网口设置和对应IP 然后查询网口驱动是否正常安装&#xff0c;输入&#xff1a; lspci | grep Ethernet 有输出说明网口驱动正常安装 然后查询电脑的ip地址&am…