【数据结构】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,(n≥0)。串中字符数目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) k≤min(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. 串的存储结构
串的存储结构与线性表相同,分为两种:顺序存储结构和链式存储结构。
- 串的顺序存储结构使用一组地址连续的存储单元来存储传中的字符序列。
- 串的链式存储结构与线性表相似,但是如果一个字符占用一个结点,就会存在很大的空间浪费。
串的链式存储结构除了在连接串与串操作时,有一定方便之外,总的来说不如顺序存储灵活,性能也不如顺序存储结构好。
对于串的顺序存储有一些变化,串值的存储空间可在程序执行过程中动态分配而得。
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"的位置。通常需要进行下面的步骤:
- 主串S第一位开始,S与T的前三个字符都匹配成功,但S的第四个字符为"d"而T的第四个字符为"g"。第一位匹配失败。
- 主串S第二位开始,S首字符为"o"而T的首字符为"g",第二位匹配失败。
- 同理,第三位和第四位都匹配失败
- 主串第五位开始,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; // 匹配失败}
相关文章:
【数据结构】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 , . . . ,…...
LAMMPS如何识别多孔结构的孔隙及其大小
关注 M r . m a t e r i a l , \color{Violet} \rm Mr.material\ , Mr.material...
JavaScript ECMAScript标准的与时俱进:从ES6至ES14的革新之路与关键技术特性剖析
ECMAScript(通常缩写为ES)是一种标准化的脚本语言规范,由ECMA International(前身为European Computer Manufacturers Association,欧洲计算机制造商协会)制定。自1997年发布首个版本以来,ECMAS…...
竞赛课第六周(树状数组的应用)
实验内容: HDU 1166 敌兵布阵【线段树】 线段树的应用 敌兵布阵 C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取…...
SpringCloud Alibaba Sentinel 实现熔断功能
一、前言 接下来是开展一系列的 SpringCloud 的学习之旅,从传统的模块之间调用,一步步的升级为 SpringCloud 模块之间的调用,此篇文章为第十六篇,即使用 Sentinel 实现熔断功能。 二、 Ribbon 系列 首先我们新建两个服务的提供者…...
开源免费AI引擎:智能合同审查技术的应用与优势
随着数字化转型的加速,合同作为商业活动中的重要法律文件,其审查和管理变得越来越重要。传统的合同审查方式耗时且容易出错,而智能AI合同审查技术的引入,为这一领域带来了革命性的变化。本文将探讨智能AI合同审查技术的应用和优势…...
易舟云凭证保存查看的3种方式
文章目录 1、保存为图片2、导出为Excel3、跨期批量导出 1、保存为图片 点击记账凭证详情,点击“下载-保存为图片”,即可下载图片! 2、导出为Excel 导出为Excel可以对单张凭证导出,也可以对指定月份的记账凭证进行批量导出。 1…...
Node.js 开发技巧
轻松创建 HTTP 服务器: 使用 Node.js,你可以轻松创建自己的 HTTP 服务器。只需几行代码,你就可以像一位传统的酒保一样为客户端提供服务。记住,不要忘记问客户端想要些什么! const http require(http);const server …...
【LeetCode】二叉树类题目详解
二叉树 二叉树的理论基础 二叉树是结点的度数之和不超过2的树,二叉树总共有五种基本形态 二叉树的种类主要有: 满二叉树完全二叉树 二叉树的存储方式 顺序存储链式存储 二叉树的遍历方式 先序遍历(深度优先搜索)中序遍历&…...
Lua语法(六)——面相对象编程
参考链接: 系列链接: Lua语法(一) 系列链接: Lua语法(二)——闭包/日期和时间 系列链接: Lua语法(三)——元表与元方法 系列链接: Lua语法(四)——协程 系列链接: Lua语法(五)——垃圾回收 系列链接: Lua语法(六)——面相对象编程 使用Lua表 进行类的模拟࿰…...
CSS-浮动文字环绕布局、隐藏属性display、overflow、三角形制作、鼠标样式
文字环绕布局 CSS文字环绕布局是指在网页中让文字环绕在图片或其他元素周围的布局方式。这通常通过CSS中的float属性来实现。你可以将图片设置为float: left;或float: right;,然后在文本元素中使用clear属性来清除浮动,以确保文字不会覆盖图片。另外&am…...
创建个人百度百科需要什么条件?
互联网时代,创建百度百科词条可以给个人带来更多的曝光和展现,相当于一个镀金的网络名片,人人都想上百度百科,但并不是人人都能创建上去的。 个人百度百科词条的创建需要满足一定的条件,今天伯乐网络传媒就来给大家聊聊…...
VR紧急情况模拟|V R体验中心加盟|元宇宙文旅
通过VR技术实现紧急情况模拟,提升安全应急能力! 简介:面对突发紧急情况,如火灾、地震、交通事故等,正确的反应和应对能够有效减少伤害和损失。为了提高人们在紧急情况下的应急能力,我们借助先进的虚拟现实…...
【Django】必须登陆才能访问功能实现
一、直接使用session传递登录状态(不推荐,但能用) 这是最简单、最直接的方法。 1.登录视图添加标识 添加登录状态标识 request.session[is_logged_in] False def user_login(request):# 这是一个登录状态标识request.session[is_logged_in] Falseif request.…...
wps使用Latex编辑公式没有Latex formula
wps使用Latex编辑公式没有Latex formula 1. 下载CTEX2. 下载LaTeXEE3. 配置Miktex4. 配置latexee5. 用管理员权限运行latexeqedit.exe6. wps插入latex公式 1. 下载CTEX 下载CTEX网址,我下载的下图这个,下载完了之后运行exe文件安装ctex。 2. 下载LaTe…...
动态指定easyui的datagrid的url
动态指定easyui的datagrid的url 重新指定datagrid url的请求方法: $("#dg").datagrid("options").url"xxx"注意,如果直接使用 $(#btnq).bind(click, function(){ $(#dg).datagrid({ url: xxx });//重新指定url$(#dg)…...
数据可视化的3D问题
三维对象非常流行,但在大多数情况下会对解释图形的准确性和速度产生负面影响。 以下是对涉及 3d 的主要图形类型的回顾,并讨论了它们是否被认为是不好的做法。 1、3D 条形图:不要 这是一个 3d 条形图。 你可能很熟悉这种图形,因为…...
使用yolov8实现自动车牌识别(教程+代码)
该项目利用了一个被标记为“YOLOv8”的目标检测模型,专门针对车牌识别任务进行训练和优化。整个系统通常分为以下几个核心步骤: 数据准备: 收集包含车牌的大量图片,并精确地标记车牌的位置和文本信息。数据集可能包含各种环境下的…...
RabbitMQ的介绍
为什么使用 MQ? 流量削峰和缓冲 如果订单系统最多能处理一万次订单,这个处理能力在足够应付正常时段的下单,但是在高峰期,可能会有两万次下单操作,订单系统只能处理一万次下单操作,剩下的一万次被阻塞。我们…...
算法-快速幂
算法-快速幂 时间复杂度 O(logk) //求 m^k mod p int qmul(int m,int k,int p) {int res1%p;while(k){if(k&1){res*m;res%p;}m*m;m%p;k>>1;}return res; }...
Flutter中工厂方法的多种实现方法与使用场景分析
在Flutter应用程序的开发中,使用工厂方法是一种常见的设计模式,它可以帮助我们更好地组织和管理代码,提高代码的可读性和可维护性。本文将介绍Flutter中工厂方法的多种实现方法,并分析其在不同场景下的使用情况。 什么是工厂方法…...
kafka(六)——存储策略
存储机制 kafka通过topic作为主题缓存数据,一个topic主题可以包括多个partition,每个partition是一个有序的队列,同一个topic的不同partiton可以分配在不同的broker(kafka服务器)。 关系图 partition分布图 名称为t…...
Linux 内核:线程的实现
在linux中的线程是轻量级线程(Light-Weight-process,LWP) 文章目录 线程概念线程实现线程拓展 线程概念 线程分类 用户级线程内核级线程,没有用户空间,完全工作在内核中(下图中没有[]的就是用户级线程&am…...
SonarQube 9.9.4 LTS社区版安装
目标 安装个SonarQube社区版. 安装SonarQube9.9.4 LTS社区版 https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-9.9.4.87374.zip # 切换到安装目录 cd /opt # 下载安装包 sudo wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube…...
Laravel 11入门:使用ServBay打造高效开发环境
Laravel 11发布,改进了不少功能。 它引入了更加流畅的应用结构、每秒限速、健康路由等特性。 此外,Laravel还推出了第一方可扩展的WebSocket服务器Laravel Reverb,为你的应用提供强大的实时功能。 在今天的指南中,我将设置一个…...
Flink WordCount实践
目录 前提条件 基本准备 批处理API实现WordCount 流处理API实现WordCount 数据源是文件 数据源是socket文本流 打包 提交到集群运行 命令行提交作业 Web UI提交作业 上传代码到gitee 前提条件 Windows安装好jdk8、Maven3、IDEA Linux安装好Flink集群,可…...
时间序列分析 # 平稳性检验和ARMA模型的识别与定阶 #R语言
掌握单位根检验的原理并能解读结果;掌握利用序列的自相关图和偏自相关图识别模型并进行初步定阶。 原始数据在文末!!! 练习1、根据某1971年9月-1993年6月澳大利亚季度常住人口变动(单位:千人)的…...
算法-日期问题
算法-日期问题 1.判断是否闰年 int is_leap(int y) {if((y%4000)||(y%40&&y%100!0)){return 1;}return 0; }2.每个月的天数 const int months[]{0,31,28,31,30,31,30,31,31,30,31,30,31};3.计算当前年当前月的天数 int get_month_days(int year,int month) {int re…...
《由浅入深学习SAP财务》:第2章 总账模块 - 2.6 定期处理 - 2.6.5 年末操作:维护新财政年度会计凭证编号范围
2.6.5 年末操作:维护新财政年度会计凭证编号范围 财务系统的维护者要在每年年末预先设置好下一年度的会计凭证编号范围(number range),以便下一年度会计凭证能够顺利生成。这一操作一定要在下一年度1月1日以前预先完成。 …...
2024年第十七届“认证杯”数学中国数学建模网络挑战赛A题思路
A题 保暖纤维的保暖能力 冬装最重要的作用是保暖,也就是阻挡温暖的人体与寒冷环境之间的热量传递。人们在不同款式的棉衣中会填充保暖材料,从古已有之的棉花,羽绒到近年来各种各样的人造纤维。不同的保暖纤维具有不同的保暖性能,比如人们以往的经验表明,高品质的羽绒具有…...
东凤网站/佛山网站建设十年乐云seo
题目描述 53.给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 示例 输入:nums [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,…...
做电影网站 需要进那些群/怎么建立网站的步骤
数据库是大难题。 MySQLRedis (中文手册 命令参考)Mongodb (中文手册)nosql文档转载于:https://www.cnblogs.com/can-H/articles/7604421.html...
wordpress 内页插件/搭建网站
原文出处: 微软互联网开发支持 Visual Studio 是一个强大的调试工具,里面很多隐藏功能少有人问津,但是在特定场景可以节省你很多时间,本文主要介绍一些Visual Studio调试相关的隐藏功能,欢迎大家补充。 运行到光标(R…...
专做国际时事评论网站/视频专用客户端app
Connection is not associated with a managed connection...
wordpress css导航/宁波seo推广公司排名
先来回顾下我们目前的进度: 加密算法的增删改查已经完成 后端 目前准备做一个加密功能函数,用来被各个执行类函数调用。 接收 url和body, 还有project_id 前端还要给普通接口、登录接口、小用例都加上 一个是否加密开关。 既然涉及到开关,那么其实也就是一个字段。 先…...
商城网站建设是什么/东莞做网站的联系电话
1.状态栏状态栏一般高度为20像素,在打手机或者显示消息时会放大到40像素高,注意,两倍高度的状态栏在好像只能在纵向的模式下使用。如下图用户可以隐藏状态栏,也可以将状态栏设置为灰色,黑色或者半透明的黑色。如果需要…...