数据结构:串 及串的模式匹配(KMP)

news2024/11/17 13:49:50

串的定义

串(String)是由零个或多个字符组成的有限序列,又名叫字符串。在计算机科学中,串是一种重要的数据结构,用于表示文本数据。串中的元素称为字符,字符可以是字母、数字或其他符号,这些字符可以是任意字符集中的成员。串是许多编程语言中的基本数据类型,用于处理文本数据。

串的基本操作

无论是基于数组还是基于链表的实现,串都支持以下基本操作:

  • StrAssign(&S, chars):赋值操作,将串chars的值赋给串S。
  • StrLength(S):求串的长度,返回串S的元素个数。
  • StrCopy(&T, S):串复制,将串S复制给串T。
  • StrConcat(&T, S1, S2):串连接,将串S1和串S2连接成新串T。
  • StrCompare(S1, S2):串比较,比较串S1和串S2,若S1=S2,则返回0;若S1<S2,则返回负数;若S1>S2,则返回正数。
  • SubString(&Sub, S, pos, len):子串获取,用Sub返回串S中第pos个字符起长度为len的子串。
  • StrInsert(&S, pos, T):串插入,将串T插入到串S的第pos个位置。
  • StrDelete(&S, pos, len):串删除,从串S中删除第pos个字符起长度为len的子串。

串的实现

串的实现方式主要有两种:基于数组(或称为顺序存储结构)和基于链表(或称为链式存储结构)。

1. 基于数组的实现

在基于数组的实现中,串的存储结构是用一个字符数组以及记录串的实际长度的变量来表示。这种实现方式可以方便地通过下标访问串中的任意字符,时间复杂度为O(1)。但是,当需要插入或删除串中的字符时,可能需要移动大量的字符,时间复杂度较高。

#define MAXLEN 255
typedef struct
{
    char ch[MAXLEN];
    int length;
}SString;

//动态分配 克服字符串超过最大长度的"截断"
typedef struct 
{
    char *ch;
    int length;
}HString;

2. 基于链表的实现

在基于链表的实现中,串的每个字符都存储在链表的一个节点中。这种实现方式在插入和删除字符时非常高效,因为只需要修改指针,时间复杂度为O(1)。但是,访问串中的任意字符时,可能需要从头节点遍历链表,时间复杂度为O(n)。

串的模式匹配:

int index(SString S,SString T){
    int i=1;
    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;
}

图解(举例):

KMP算法:

(关于KMp算法中如何求next数组以及 算法中的指针变换在我写的另一篇博客中) 

几个概念:

1、字符串的前缀、后缀和部分匹配值
要了解子串的结构,首先要弄清楚几个概念:前缀、后缀和部分匹配值。前缀指除最后一个字符以外,字符串的所有头部子串;后缀指除第一个字符外,字符串的所有尾部子串;部分匹配值则为字符串的前缀和后缀的最长相等前后缀长度。下面以' ababa '为例进行说明:
' a '的前缀和后缀都为空集,最长相等前后缀长度为0。
' ab '的前缀为{ a },后缀为( b },{ a } ∩ { b }=0,最长相等前后缀长度为0。

' aba '的前缀为{ a  ab ),后缀为( a , ba },( a , ab } ∩ { a , ba }={ a ),最长相等前后缀长度为1。
' abab '的前缀( a , ab , aba } ∩ 后缀( b , ab , bab }={ ab ),最长相等前后缀长度为2。
' ababa '的前缀{ a , ab , aba , abab ) ∩ 后缀( a , ba , aba , baba }={ a , aba ),公共元素有两个,最长相等前后缀长度为3。
因此,字符串' ababa 的部分匹配值(PM表为00123。

右移位数=已匹配的字符数-对应的部分匹配值
Move=(j-1)-PM[j-i]

next数组就是将PM表的值全部向右移1位,即字符串' ababa 的next值为-1 0 0 1 2(这样哪个元素匹配失败就直接找next数组里自己对应的值,不用去找匹配失败元素的前一个元素的PM值。

Move=(j-1)-next[j]
//j回退到
j=j-Move=j-((j-1)-next[j])=next[j]+1

为了使公式更加简洁/计算方便可以将next数组整体+1,即 即字符串' ababa 的next值为0 1 1 2 3

实现过程:

为了避免朴素算法的低效,几位计算机科学家辈D.E.Knuth、J.H.MorTis和V.R.Pratt发表了一个模式匹配算法,可以一定程度上避免重复遍历的问题,该算法就是大名鼎鼎的KMP算法。

从暴力匹配到KMP
要理解KMP算法的原理,我们首先需要批判一下朴素算法中有哪些做的不好的地方。

我们将之前的朴素算法的匹配过程合起来看如下面的图所示:

我们可以发现,在2、3、4步骤中,主串的首字符与子串的首字符均不等。我们仔细观察可以发现,对于子串"google"来说,首字母"g"与后面的两个字母是不相等的,而在步骤1中主串与子串的前三个字符相等,这就意味着子串的首字符"g"不可能与主串的二、三位相等,故上图中步骤2、3完全是多余的。

也就是说,如果我们知道子串的首字符"g"与后面两个字符不相等(此处需要进行一些预处理,这是后面的重点),我们就不需要再进行2、3步操作,只保留1、4、5步即可。


从上面这幅图我们可以惊喜地发现,在使用朴素算法进行匹配时,主串指针需要进行一些回退;而在知道了子串的一些性质后,主串指针不需要再进行回退,只需一直往前走就行,这样就节省了一些时间开销。

你或许还会有疑问,主串指针是不需要回退了,但为啥我的子串指针还要一直回退到开头呢,有没有办法避免这样呢?

当然是有的,我们再举一个例子,假设主串S=“abcababca”,子串T=“abcabx”,那我们采用朴素算法在进行模式匹配的步骤如下所示:


由之前一个例子的分析可知由于T串的首字母"a"与之后的字母"b"、"c"不等,故步骤2、3均是可以省略的。

又因为T的首位"a"与T第四位的"a"相等,第二位的"b"与第五位的"b"相等。而在步骤1中,第四位的"a"与第五位的"b"已经与主串s中的相应位置比较过了是相等的。因此可以断定, T的首字符“a”、第二位的字符“b”与S的第四位字符和第五位字符也不需要比较了,肯定也是相等的。所以步骤4、5这两个比较得出字符相等的步骤也可以省略。

所以在这个例子中我们模式匹配的流程为:


在这个例子中我们发现,在得知了子串中有相等的前后缀之后,匹配失败时子串指针不需要回退到开头处,而是回退到相等前缀的后一个位置。

补充一下前后缀的概念:前缀指的是不包含尾字符的所有子串;后缀指的是不包含首字符的所有子串

对比两个例子中朴素做法与改进做法的差别,我们可以发现这两种方法的相同点为都是依靠两个指针的移动对比来实现字符串模式匹配,不同之处在于在改进做法中主串的指针i不需要进行回溯,子串的指针j会根据子串中的最长相等前后缀来进行回溯,这样就省去了一些不需要的回溯步骤,从而降低了时间复杂度。

注意事项

  • 在使用串时,应注意串的边界条件,如索引越界等问题。
  • 根据不同的应用场景,选择合适的串实现方式,以达到最优的性能。
  • 在某些编程语言中,字符串的实现可能更加复杂,如Python中的字符串是不可变的,这意呀着每次修改字符串都会生成一个新的字符串对象。

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

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

相关文章

【LeetCode: 227. 基本计算器 II + 栈】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

C++ | Leetcode C++题解之第445题两数相加II

题目&#xff1a; 题解&#xff1a; class Solution { public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {stack<int> s1, s2;while (l1) {s1.push(l1 -> val);l1 l1 -> next;}while (l2) {s2.push(l2 -> val);l2 l2 -> next;}int carry 0;L…

STM32-HAL库驱动DHT11温湿度传感器 --2024.9.28

目录 一、教程简介 二、驱动原理讲解 &#xff08;一&#xff09;通信4步骤 &#xff08;二&#xff09;传感器数据解析 三、CubeMX生成底层代码 &#xff08;一&#xff09;基础配置 &#xff08;二&#xff09;配置DHT11的驱动引脚 &#xff08;三&#xff09;配置串口 四…

基于webComponents的纯原生前端框架

我本人的个人开发web前端前框架xui&#xff0c;正在开发中&#xff0c;业已完成50%的核心开发工作&#xff0c;并且在开发过程中逐渐完善. 目前框架未采用任何和市面上框架模式&#xff0c;没有打包过程&#xff0c;实现真实的开箱即用。 当然在开发过程中也会发现没有打包工…

二阶低通滤波器Simulink仿真测试

1、如何将S域传递函数转为Z域传递函数 传递函数如何转化为差分方程_非差分方程转成差分方程-CSDN博客文章浏览阅读4.1k次,点赞4次,收藏50次。本文介绍了如何将传递函数转化为差分方程,主要适用于PLC和嵌入式系统。通过MATLAB的系统辨识工具箱获取传递函数,并探讨了离散化方…

通义灵码【最佳编程实践】说明

通义灵码是JetBrains或VSCode集成开发环境&#xff08;IDE&#xff09;中嵌入的一款智能开发助手工具&#xff0c;旨在通过人工智能技术简化软件开发过程&#xff0c;提升开发效率。本文将介绍在开发过程中如何深度体验多种辅助功能。其主要功能包括&#xff1a;通用大模型问答…

AdaptIoT——制造业中使用因果关系的自我标签系统

0.概述 论文地址&#xff1a;https://arxiv.org/abs/2404.05976 在许多制造应用中&#xff0c;机器学习&#xff08;ML&#xff09;已被证明可以提高生产率。针对制造业应用提出了一些软件和工业物联网&#xff08;IIoT&#xff09;系统&#xff0c;以接收这些 ML 应用。最近&…

2-仙灵之谜(安装钱包及添加网络)

2-仙灵之谜&#xff08;安装钱包及添加网络&#xff09; 前言&#xff08;该游戏仅供娱乐&#xff09;正文 前言&#xff08;该游戏仅供娱乐&#xff09; 新部署的相关链接如下 游戏访问地址&#xff1a;   http://8.130.162.58/ RPC网络&#xff1a;   网络名称 &#xf…

目前最好用的爬虫软件是那个?

作为一名数据工程师&#xff0c;三天两头要采集数据&#xff0c;用过十几种爬虫软件&#xff0c;也用过Python爬虫库&#xff0c;还是建议新手使用现成的软件比较方便。 这里推荐3款不错的自动化爬虫工具&#xff0c;八爪鱼、亮数据、Web Scraper 1. 八爪鱼爬虫 八爪鱼爬虫是一…

尚硅谷vue3+TypeScript笔记大全

源码及视频&#xff1a;通过百度网盘分享的文件&#xff1a;尚硅谷vue3 链接&#xff1a;https://pan.baidu.com/s/19zVM5Xsr1UP7tZ4D0mhVqA?pwdgmta 提取码&#xff1a;gmta 1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piec…

演示:基于WPF的DrawingVisual开发的频谱图和律动图

一、目的&#xff1a;基于WPF的DrawingVisual开发的频谱图和律动图 二、效果演示 波形图 极坐标 律动图极坐标图 律动图柱状图 Dock布局组合效果 三、环境 VS2022,Net7,Win10&#xff0c;NVIDIA RTX A2000 四、主要功能 支持设置起始频率&#xff0c;终止频率&#xff0c;中心…

我把「国产Sora」接入了「小爱」,邀你免费体验

前段时间&#xff0c;搞了个微信 AI 小助理-小爱(AI)&#xff0c;爸妈玩的不亦乐乎。 零风险&#xff01;零费用&#xff01;我把AI接入微信群&#xff0c;爸妈玩嗨了&#xff0c;附教程&#xff08;下&#xff09; 不仅可以智能问答&#xff0c;文生图的能力也接了进来&…

HTML+CSS 水滴登录页

文章目录 一、效果演示二、Code1.HTML2.CSS 三、实现思路拆分 一、效果演示 实现了一个水滴登录页的效果。页面包含一个水滴形状的登录框和两个按钮&#xff0c;登录框包括用户名、密码和登录按钮&#xff0c;按钮分别为忘记密码和注册。整个页面的设计非常有创意&#xff0c;采…

每日一练 2024.9.29(1)

目录 解题思路与代码实现 一、问题分析 二、解题策略 伪代码&#xff1a; 三、代码实现 四、代码解析 五、解题过程总结 六、运行过程示例 示例 1&#xff1a; 示例 2&#xff1a; 七、对比分析 八、改进建议 九、总结 解题思路与代码实现 这道题目要求我们模拟买…

如何解决调试dev-出++5.11不成功问题

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

【CKA】二、节点管理-设置节点不可用

2、节点管理-设置节点不可用 1. 考题内容&#xff1a; 2. 答题思路&#xff1a; 先设置节点不可用&#xff0c;然后驱逐节点上的pod 这道题就两条命令&#xff0c;直接背熟就行。 也可以查看帮助 kubectl cordon -h kubectl drain -h 参数详情&#xff1a; –delete-empty…

一个家越来越有钱,是因为女人身上有这3个好习惯!

在一个家庭中&#xff0c;女人往往扮演着举足轻重的角色。 她们不仅是家庭的支柱&#xff0c;也是家庭和谐与繁荣的重要因素。 正所谓“家和万事兴”&#xff0c;一个家庭是否能够兴旺发达&#xff0c;与家中女人的习惯和态度息息相关。 实际上&#xff0c;一个家越来越有钱…

ubuntu20.04安装CUDA与cudnn

这里写目录标题 一、NVIDIA显卡驱动安装二、安装CUDA官网找对应版本下载安装文件安装配置环境变量 三、安装cuDNN选择版本另一种下载方式 四、cuDNN与CUDA关系CUDAcuDNN的依赖关系与CPU的交互开发编程角度图示 总结 一、NVIDIA显卡驱动安装 我这里之前就装好了 使用命令 nvid…

DCDC电源设计工具(软件)(二)—— DC/DC Designer(MPS)

目录 一、简介 二、主页介绍 1、芯片选择 &#xff08;1&#xff09;在数据库中选择芯片 &#xff08;2&#xff09;通过下拉列表/具体型号选择芯片 2、编辑参数 &#xff08;1&#xff09;输入主要规格 &#xff08;2&#xff09;输入可选规格 &#xff08;3&…

基于Arduino的自弹尤克里里机器人

需要项目源码资料的可以私信我 基于Arduino的自弹尤克里里机器人 一、简介二、材料清单三、工具四、实现过程步骤1&#xff1a;实物图步骤2&#xff1a;3D打印部件步骤3&#xff1a;组装上半部分步骤4&#xff1a;组装下半部分步骤5&#xff1a;安装导轨步骤6&#xff1a;设置…