【数据结构】04串

  • 1. 定义
  • 2. 串的比较
  • 3. 串的存储结构
  • 4. 具体实现
  • 5. 模式匹配
    • 5.1 常规思路实现
    • 5.2 KMP模式匹配算法
      • 5.2.1 next数组计算
      • 5.2.1 代码计算next数组
      • 5.2.2 KMP算法实现

1. 定义

串(string)是由零个或多个字符组成的有限序列,又叫字符串。
一般记为s= a 1 , a 2 , . . . , a n , ( n ≥ 0 ) a_1,a_2,...,a_n,(n\ge0) a1,a2,...,an,(n0)。串中字符数目n称为串的长度。零个字符的串称为空串。

2. 串的比较

给定两个串:s= a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,t= b 1 , b 2 , . . . , b m b_1,b_2,...,b_m b1,b2,...,bm,当满足以下条件之一时, s < t s<t s<t
(1) n < m n<m n<m,且 a i = b i a_i=b_i ai=bi,例如: s = h a p , t = h a p p y s=hap,t=happy s=hap,t=happy,就有 s < t s<t s<t
(2)存在某个 k ≤ min ⁡ ( m , n ) k\le\min(m,n) kmin(m,n),使得 a i = b i a_i=b_i ai=bi a k < b k a_k<b_k ak<bk,就有 s < t s<t s<t。例如 s = h a p p e n s=happen s=happen t = h a p p y t=happy t=happy,此时 k = 4 k=4 k=4,且字符有: e < y e<y e<y,则 s < t s<t s<t

3. 串的存储结构

串的存储结构与线性表相同,分为两种:顺序存储结构和链式存储结构。

  1. 串的顺序存储结构使用一组地址连续的存储单元来存储传中的字符序列。
  2. 串的链式存储结构与线性表相似,但是如果一个字符占用一个结点,就会存在很大的空间浪费。
    串的链式存储结构除了在连接串与串操作时,有一定方便之外,总的来说不如顺序存储灵活,性能也不如顺序存储结构好。

对于串的顺序存储有一些变化,串值的存储空间可在程序执行过程中动态分配而得。

4. 具体实现

为了方便管理,利用C++的类实现了String:

#include <iostream>
using namespace std;class String
{friend ostream& operator<<(ostream& os, const String& s); // 友元函数
private:int max_size = 10;int extend_size = 10;char* ptr; // 字符指针int len;// 扩展内存void ExtendString(){char* new_ptr = new char[max_size + extend_size];memset(new_ptr, 0, sizeof(char) * (max_size + extend_size));// 拷贝数据memcpy(new_ptr, ptr, sizeof(char) * max_size);// 清空内存delete[] ptr;// 指向新内存ptr = new_ptr;// 更新最长大小max_size = max_size + extend_size;}public:String()  // 构造函数{// cout << "调用了默认构造函数" << endl;ptr = new char[max_size]; // 初始化内存memset(ptr, 0, sizeof(char) * max_size);len = 0; // 字符串长度}String(const char* s) // 构造函数{cout << "调用了构造函数" << endl;int len = strlen(s);ptr = new char[this->max_size]; // 初始化内存memset(ptr, 0, sizeof(char) * this->max_size);while (this->max_size < len){ExtendString();}// 拷贝数据memcpy(this->ptr, s, sizeof(char) * len);this->len = len;}String(const String& s) // 构造函数{cout << "调用了拷贝构造函数" << endl;ptr = new char[s.max_size]; // 初始化内存memset(ptr, 0, sizeof(char) * s.max_size);// 拷贝数据memcpy(ptr, s.ptr, sizeof(char) * s.len); // 拷贝字符串len = s.len;max_size = s.max_size;}String& operator=(const char* str) // 赋值函数{cout << "调用了重载的赋值函数char*" << endl;int len = strlen(str);while (strlen(str) > this->max_size){ExtendString();}memcpy(this->ptr, str, sizeof(char) * len);this->len = len;return *this;}String& operator=(const String& str) // 赋值函数{cout << "调用了重载的赋值函数String" << endl;while (str.max_size> this->max_size){ExtendString();}memcpy(this->ptr, str.ptr, sizeof(char) * str.len);this->len = str.len;return *this;}// 清空字符串void clear() {memset(ptr, 0, sizeof(char)*max_size); // 内存置0len = 0;}// 返回字符串长度int length()const {return len; }// 返回从第pos个字符开始,长度为len的子串String sub(int pos, int len) {// 创建临时对象String tmp;for (int i = 0; i < len; i++){tmp.ptr[i] = this->ptr[i + pos - 1];}tmp.len = len;return tmp;}// 将str插入到当前字符第pos个字符之后String Insert(int pos, const String& str){int whole_len = str.len + this->len;while (this->max_size < whole_len){ExtendString();}// 插入字符// 1.保存第pos个字符之后的所有字符,后移str_len位 即:pos-1开始的len-pos个字符移动到pos+str_len-1for (int i = this->len; i > pos; i--){this->ptr[i + str.len - 1] = this->ptr[i - 1]; // }// 2.插入字符从 str.ptr[0]到 str.ptr[len-1] 到pos-1至pos+str.len-1;memcpy(&(this->ptr[pos]), &(str.ptr[0]), sizeof(char) * str.len);this->len = whole_len;return *this;}~String(){delete[] ptr;ptr = nullptr;len = 0;}bool operator<(const String& str)const{int i = 0;for (i = 0; i < this->len && i < str.len; i++){if (this->ptr[i] == str.ptr[i]){continue;}if (this->ptr[i] > str.ptr[i]) // 前面都相等,一旦有一个字符大,则整个字符串都大{return false;}else // 前面都相等,当前字符小{return true;}}if (this->len < str.len)// 退出比较的原因是前面字符都相等,但当前字符串的字符较短{return true;}return false;}bool operator>(const String& str)const{int i = 0;for (i = 0; i < this->len && i < str.len; i++){if (this->ptr[i] == str.ptr[i]){continue;}if (this->ptr[i] < str.ptr[i]) // 前面都相等,一旦有一个字符小,则整个字符串都小{return false;}else // 前面都相等,当前字符大{return true;}}if (this->len > str.len)// 退出比较的原因是前面字符都相等,但当前字符串的字符更长{return true;}return false;}
};// 重载输出
ostream& operator<<(ostream& os, const String& s)
{for (int i = 0; i < s.len; i++){os << s.ptr[i];};return os;
}int main(void)
{String s1;s1 = "ace";String s2 = s1;String s3("bdf");String s4 = "asw";  // 自动类型转换,调用赋值函数/*s3 = s2.sub(1, 2);*/cout << s1 << endl;s1.clear();cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout <<( s3 < s2) << endl;s2.Insert(3, s3);cout << s2 << endl;return 0;
}

5. 模式匹配

在一段字符串中去定位子串的位置的操作称作串的模式匹配,这是串中最重要的操作之一。
假设我们要从主串"S=goodgoogle"中,找到子串"T=google"的位置。通常需要进行下面的步骤:

  1. 主串S第一位开始,S与T的前三个字符都匹配成功,但S的第四个字符为"d"而T的第四个字符为"g"。第一位匹配失败。
  2. 主串S第二位开始,S首字符为"o"而T的首字符为"g",第二位匹配失败。
  3. 同理,第三位和第四位都匹配失败
  4. 主串第五位开始,S与T,6个字母全匹配,匹配成功。

简单来说,对主串的每个字符作为子串开头,与要匹配的子串进行匹配。对主串做外部循环,每个字符开头做子串T长度的内部循环,直到匹配成功或主串遍历完成为止。

5.1 常规思路实现

		int Index(const String& T){int i, j = 0;for (i = 0; i < this->len; i++){for (j = 0; j < T.len; j++){if (this->ptr[i+j] == T.ptr[j]) // 主串以i开头的T长度的子串匹配{continue;}break;}if (j == T.len) // 子串匹配成功{return i; // 返回此时子串在主串中的位置}}return -1; // 匹配失败}

5.2 KMP模式匹配算法

常规思路的匹配算法需要挨个遍历主串和子串,效率低效。KMP算法的思路是当发现某一个字符不匹配的时候,由于已经知道之前遍历过的字符,利用这些信息避免暴力算法中"回退"的步骤。
KMP算法的原理:
1)在匹配过程中,主串的指针不需要回溯,只回溯子串的指针。
2)如果子串和主串中前n个字符匹配成功,遇到匹配失败的字符时,子串回溯的下标由子串的内容决定(回溯到:匹配失败前,子串内容最长相等前后缀 的长度),然后继续比较。
前缀:包含首位字符但不包含末位字符的子串。如:ababa的前缀包括:a,ab,aba,abab
后缀:包含末位字符但不包含首位字符的子串。如:ababa的后缀包括:a,ba,aba,baba。
则对于字符串:ababa最长公共前后缀为:aba。
具体流程:依次匹配主串和子串的字符,当遇到子串与主串字符不匹配时,子串指针回溯,并从回溯的位置继续与主串当前位置字符比较。如图中所示:ABABABCAA与ABABC匹配,当遇到主串字符A时,子串字符为C,此时无法匹配,子串指针回溯到第3个字符即A处,继续比较。KMP算法的关键是如何获取子串回溯位置,即next数组。
在这里插入图片描述

5.2.1 next数组计算

对于子串:ABABC
第一个字符A,没有前缀没有后缀,对应的next数组值为0。
第二个字符AB,包含的前后缀有:A B 对应的next数组值为1。
第三个字符的子串为ABA,包含的前后缀有:A A AB BA ,最长的长度为1。则next数组值为1。
第四个字符的子串为ABAB,包含的前后缀有:A B AB AB ABA BAB,最长的公共前后缀长度为2。next数组值为2。
第五个字符的子串为ABABC,包含的前后缀有:A C AB BC ABA ABC ABABA BABC,最长的公共前后缀长度为0。next数组值为0。
因此对于ABABC的next数组值为:0,1,1,2,0。

对于子串:AABAAF计算next数组:
初始化:i 和 j 其中i指向的是后缀末尾,j指向的是前缀末尾即把S[0,j]看作是子串,把S[1,i]看作是主串来理解。初始时j=0,i=1;next[0]=0。
前后缀不同的情况:S[i]!=S[j],此时需要查询next[j-1]的值,查询到j需要回退的位置,必须要满足j>0,直到
S[i]==S[j]或者回退到初始位置。
前后缀相同的情况:S[i]==S[j],j++。
更新next数组:next[i]=j。
模拟运行:

  • 初始化:next[0] = 0. i =1 ,j =0.
  • i=1,j=0.S[0]=A==S[1]=A => j++,j=1. next[1]=1.
  • i=2,j=1;S[1]=A != S[2]= B => j=next[j-1]=next[0]=0 ,S[0]!=S[2] j>0. next[2] = 0.
  • i=3,j=0;S[0] =A ==S[3]=A => j++,j=1. next[3] = 1.
  • i=4,j=1;S[1]=A == S[4]= A => j++,j=2. next[4] = 2.
  • i=5,j=2;S[2]=B!=S[5]=F =>j = next[j-1]=next[1] = 1 ,S[1]!=S[5] j=next[j-1]=next[0]=0 j>0.next[5]=0.
    在这里插入图片描述

5.2.1 代码计算next数组

	// 获取next数组int* getNext(){// 创建next数组,长度与字符串长度一致delete[] next;next = new int[this->len];memset(next, 0, sizeof(int) * this->len);// 初始化int i, j = 0;next[0] = 0;for (i = 1; i < this->len; i++){// 前后缀不相同的情况while (j > 0 && ptr[i] != ptr[j]){// 回溯jj = next[j - 1];}// 前后缀相同的情况if (ptr[i] == ptr[j]){j++;}// 更新nextnext[i] = j;}return next;}

5.2.2 KMP算法实现

/ KMP算法int KMP(String& T){// 获取子串next数组int* next_ptr = T.getNext();// 开始匹配int i = 0, j = 0;while (i < len){if (ptr[i] == T.ptr[j]) // 匹配成功{i++; j++;}else if (j > 0) // 匹配失败,子串指针回溯,并且需要保证回溯位置不越界(即无法回溯到首个字符前){j = next_ptr[j - 1];}else // 子串第一个字符就匹配失败i++;if (j == T.len)// 子串已经到达末尾,即全部匹配上{return i - j; // 返回位置}}return -1; // 匹配失败}

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

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

相关文章

NGO-VMD+皮尔逊系数+小波阈值降噪+重构

NGO-VMD皮尔逊系数小波阈值降噪重构 NGO-VMD皮尔逊系数小波阈值降噪重构代码获取戳此处代码获取戳此处 以西储大学轴承数据为例&#xff0c;进行VMD&#xff0c;且采用NGO进行K a参数寻优 并对分解分量计算皮尔逊相关系数筛选含噪声分量&#xff0c;对其进行小波软硬阈值降噪&a…

C/C++内存泄漏及检测

“该死系统存在内存泄漏问题”&#xff0c;项目中由于各方面因素&#xff0c;总是有人抱怨存在内存泄漏&#xff0c;系统长时间运行之后&#xff0c;可用内存越来越少&#xff0c;甚至导致了某些服务失败。内存泄漏是最难发现的常见错误之一&#xff0c;因为除非用完内存或调用…

【JAVA基础篇教学】第十篇:Java中Map详解说明

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第十篇&#xff1a;Java中Map详解说明。 在 Java 编程中&#xff0c;Map 接口代表了一种键值对的集合&#xff0c;每个键对应一个值。Map 接口提供了一系列操作方法&#xff0c;可以方便地对键值对进行增删改查等操作。本…

模板方法模式:定义算法骨架的设计策略

在软件开发中&#xff0c;模板方法模式是一种行为型设计模式&#xff0c;它在父类中定义一个操作的算法框架&#xff0c;允许子类在不改变算法结构的情况下重定义算法的某些步骤。这种模式是基于继承的基本原则&#xff0c;通过抽象类达到代码复用的目的。本文将详细介绍模板方…

NASA数据集——亚洲夏季季风化学与气候影响项目超高灵敏度气溶胶光谱(UHSAS)数据

ACCLIP_Aerosol_AircraftInSitu_WB57_Data 简介 ACCLIP_Aerosol_AircraftInSitu_WB57_Data 是亚洲夏季季风化学与气候影响项目&#xff08;ACCLIP&#xff09;期间收集的原地气溶胶数据。本数据集收录了来自下一代激光质谱仪&#xff08;PALMS-NG&#xff09;、单颗粒烟尘光度…

一文读懂Partisia Blockchain,被严重低估的隐私区块链生态

在今年 3 月&#xff0c;隐私公链 Partisia Blockchain 迎来了重要的进展&#xff0c;该生态通证 $MPC 上线了交易所&#xff0c;目前 $MPC 通证可以在 Kucoin、Gate、BitMart、Bitfinex、Bitture 等平台交易&#xff0c;并将在不久后上线 MEXC 平台。 ​ 在上个月上线市场至今…

【项目实战】记录一次PG数据库迁移至GaussDB测试(上)

目录 一、说明 1.1、参考文档 1.2、注意事项 1.3、环境基本情况 二、GaussDB新环境安装 2.1 配置操作环境变量 2.1.1 关闭防火墙 步骤1 执行以下命令&#xff0c;检查防火墙是否关闭。 步骤2 执行以下命令&#xff0c;关闭防火墙并禁止开机启动。 步骤3 修改/etc/sel…

单细胞RNA测序(scRNA-seq)Cellranger流程入门和数据质控

单细胞RNA测序(scRNA-seq)Cellranger流程入门和数据质控 单细胞RNA测序(scRNA-seq)基础知识可查看以下文章: 单细胞RNA测序(scRNA-seq)工作流程入门 单细胞RNA测序(scRNA-seq)细胞分离与扩增 1. 单细胞RNA-seq样本数据说明 样本数据来源文章:Acquired cancer re…

【计算机毕业设计】基于微信小程序的开发项目150套(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f9e1;今天给大家分享200的微信小程序毕业设计&#xff0c;后台用Java开发&#xff0c;这些项目都经过精心挑选&#xff0c;涵盖了不同的实战主题和用例&#xff0c;可做毕业设…

MQ概览及Kafka详解

文章目录 概览MQ优点MQ缺点常见MQ对比JMS消息模型点对点模式发布订阅模式 kafka基础架构发布订阅工作流程生产者生产者文件存储生产者分区策略生产者数据可靠性保证生产者数据一致性保证生产者ack机制ExactlyOnce生产者发送消息流程 消费者消费者分区分配策略消费者消费数据问题…

算法设计与分析实验报告c++实现(TSP问题、哈夫曼编码问题、顾客安排问题、最小生成树问题、图着色问题)

一、实验目的 1&#xff0e;加深学生对贪心算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 用贪心算…

streamlit 大模型前段界面

结合 langchain 一起使用的工具&#xff0c;可以显示 web 界面 pip install streamlit duckduckgo-search 运行命令 streamlit run D:\Python_project\NLP\大模型学习\test.py import os from dotenv import load_dotenv from langchain_community.llms import Tongyi load…

matlab学习002-函数及流程控制语句

目录 一&#xff0c;matlab编程基础 1&#xff09;matlab脚本和函数文件 ①脚本文件 ②函数文件 2&#xff09;函数的定义和调用 ①定义 ②调用 3&#xff09;程序流程控制 ①使用for求 122^2……2^622^63之和 ②使用while语句求122^2……2^622^63之和 ③使用matl…

Python学习(三)

函数扩展 多返回值 参数扩展 位置参数 注意:传递的参数和定义的参数的顺序及个数必须一致 关键字参数 关键字名称必须和形参名称相同&#xff0c;形参叫name&#xff0c;那么关键字也要写name 不定长参数 缺省参数

【优选算法专栏】专题十:哈希表(一)

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

mutable关键字的作用(c++)

常成员变量、常成员函数与常对象 常成员变量 声明为常成员变量的成员变量&#xff0c;在对象被创建后就不能被修改常成员变量必须在对象的构造函数初始化列表中赋值&#xff0c;不能在构造函数体中赋值初始化列表的执行是在函数体执行之前就执行了的。上面这种写法和下面的写…

卫星遥感影像在农业方面的应用及评价

一、引言 随着科技的进步&#xff0c;卫星遥感技术在农业领域的应用越来越广泛。卫星遥感技术以其宏观、快速、准确的特点&#xff0c;为农业生产和管理提供了有力的技术支撑。本文将对卫星遥感在农业方面的应用进行详细介绍&#xff0c;并通过具体案例进行说明。 二、…

(二)ffmpeg 下载安装以及拉流推流示例

一、ffmpeg下载安装 官网&#xff1a;https://www.ffmpeg.org/ 源码下载地址&#xff1a;https://www.ffmpeg.org/download.html#releases 下载源码压缩包 下载完成之后解压并在该目录下打开命令窗口 安装依赖环境&#xff1a; sudo apt-get install build-essential nasm …

【CAD建模号】学习笔记(二)——工作区

工作区介绍 工作区由[工具提示]&#xff0c;[自适应网格]&#xff0c;[自适应坐标轴]&#xff0c;[参考坐标轴]&#xff0c;[绘制的图形]组成。 一、工具提示 工具提示是提示当前工具的操作步骤&#xff0c;同时也提供了更加精确的参数输入方式建模、绘图时直接生成面&#x…

规则系统架构

规则系统架构 目录概述需求&#xff1a; 设计思路实现思路分析1.规则系统架构2. 规则系统架构优势 性能参数测试&#xff1a; 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,mak…