【数据结构】串

news2024/11/17 19:48:37

  • 串的顺序实现
  • 简单的模式匹配算法
  • KMP算法
  • KMP算法的进一步优化

串的顺序实现

初始化

#define MaxSize 50
typedef char ElemType;

//顺序存储表示
typedef struct{
    ElemType data[MaxSize];
    int length;
}SString;

/**
 * 初始化串
 */
void InitString(SString *string) {
    for (int i = 0; i < string->length; ++i) {//全部初始化为‘\0’
        string->data[i] = '\0';
    }
    string->length = 0;//长度赋值为0
}

赋值操作

/**
 * 赋值操作
 */
void StrAssign(SString *string, char *str) {
    //循环赋值
    for (int i = 0; i < strlen(str); ++i) {
        string->data[i] = str[i];
    }
    string->length = strlen(str);
}

复制操作

/**
 * 复制操作:把串 str 复制到 string
 */
void StrCopy(SString *string, SString str) {
    int i, j;
    for (i = 0; i < strlen(str.data); ++i) {
        string->data[i] = str.data[i];
    }
    string->length = str.length;
}

判空操作

/**
 * 判空操作
 */
bool StrIsEmpty(SString string) {
    if (string.length == 0) {
        return true;
    } else {
        return false;
    }
}

比较操作

/**
 * 比较操作:首先比较同位序的字母大小,其次比较字符串的长度大小
 */
int StrCompare(SString string, SString str) {
    for (int i = 0; i < string.length && i < str.length; ++i) {
        if (string.data[i] != str.data[i]) {
            return string.data[i] - str.data[i];
        }
    }
    return string.length - str.length;
}

截取操作

/**
 * 截取操作:用 sub 返回串 string 的第 pos 个字符起长度为 len 的子串
 */
bool SubString(SString *sub, SString string, int pos, int len) {
    if (pos + len > string.length) {
        return false;
    }
    for (int i = pos; i < pos + len; ++i) {
        sub->data[i - pos] = string.data[i];
    }
    sub->length = len;
    return true;
}

连接操作

/**
 * 连接操作:把两个串相连
 */
void StrConcat(SString *sub, SString string, SString str) {
    int i;
    for (i = 0; i < string.length; ++i) {
        sub->data[i] = string.data[i];
    }
    for (int j = 0; j < str.length; ++j, ++i) {
        sub->data[i] = str.data[j];
    }
    sub->length = string.length + str.length;
}

简单的模式匹配算法

所谓串的模式匹配算法,是一种专门定位子串在主串中位置的方法(方案、思想),整个定位的过程称为模式匹配。此外,在模式匹配的过程中,子串通常又被称为“模式串”。

串模式匹配的实现方法有很多种,本节先给大家讲一种最容易理解、最简单的方法,称为 BF 算法。

采用 BF 算法定位模式串在主串中的位置,就是简单粗暴的从主串的起始位置开始,不断地将模式串中的字符和主串中的字符进行对比。

具体来讲,假设对模式串 A(abcac)和主串 B(ababcabacabab)进行模式匹配,BF 算法的执行过程如下:


将模式串 A 与主串 B 的首字符对齐,逐个判断相对的字符是否相等
在这里插入图片描述
由于模式串 A 与主串 B 的第 3 个字符匹配失败,此时将模式串 A 后移一个字符的位置,采用同样的方法重新匹配
在这里插入图片描述
两个串依旧匹配失败,模式串 A 继续后移一个字符的位置
在这里插入图片描述
模式串 A 继续向后移动
在这里插入图片描述
模式串 A 与主串 B 共匹配了 6 次才成功,最终定位到模式串 A 位于主串 B 第 6 的位置处,整个模式匹配的过程就称为 BF 算法。

int Index(SString s,SString t){//s:主串,t:子串
    int i = 0,j = 0;
    while (i<=s.length&&j<=t.length)
    {
        if (s.data[i]==t.data[j])
        {
            ++i;++j;
        }else{
            i = i - j + 2;
            j = 0;
        }
    }
    if (j>t.length)
    {
        return i - t.length;
    }else{
        return 0;
    }
}

KMP算法

学过 BF 算法的读者应该知道,该算法的实现思想很简单,就是 “傻瓜式” 地将模式串(假定为子串的串)与主串中的字符一一匹配。KMP 算法不一样,它可以略过一些原本就不可能成功的匹配过程,提高模式匹配的效率。

例如,对主串 A(“ABCABCE”)和模式串 B(“ABCE”)进行模式匹配,KMP 算法只需要匹配 2 次。
在这里插入图片描述
显示第一次匹配失败,从整个匹配过程可以获得的信息是:模式串中 “ABC” 和主串对应的字符相同,但模式串中的字符 ‘A’ 与 ‘B’ 和 ‘C’ 不同。这也就意味着,下次模式匹配时没必要再让串 B 中的 ‘A’ 与主串中的字符 ‘B’ 和 ‘C’ 一一匹配,它们绝不可能相等。

因此第二次模式匹配开始前,我们改变指针 j 的指向

在这里插入图片描述
模式串直接跳过主串中的第 2、3 个字符,从第 4 个字符处开始第二次模式匹配,最终匹配成功。KMP 算法的整个匹配过程只进行了 2 次,而如果用 BF 算法需要匹配 4 次。

和 BF 算法相比,KMP 算法只需要移动指针 j 的位置,可以略过一些原本就不可能成功的匹配过程,减少匹配的次数,提高模式匹配的效率。


对于初学者而言,KMP 算法最大的难点是:当模式匹配失败后,如何修改指针 j 的位置。

请大家先记住一句话:指针 j 的新位置只和模式串有关,与主串无关。接下来通过一个实例,给大家演示如何只通过模式串确定指针 j 的位置。

将模式串 B 改为 “ABCAE”,第一次匹配的过程如下图所示:

在这里插入图片描述
匹配失败时模式串中字符 ‘E’ 前的 ‘A’ 与模式串开头的 ‘A’ 相等,因此我们可以将指针 j 指向模式串中的第 2 个字符,下次直接从 i 和 j 的位置开始匹配,这就是 KMP 算法重定位指针 j 的方法。

也就是说,模式匹配失败后指针 j 的新位置可以通过匹配失败位置前的字符计算得出。进一步讲,只要给定一个模式串,我们就可以确定匹配失败后指针 j 的新位置。

当模式串和主串进行模式匹配时,模式串中的每个字符都可能导致匹配失败,而失败后指针 j 的新位置是可以计算出来的。模式串中有多少个字符,就可以计算出多少个指针 j 的新位置,它们是一一对应的关系。我们通常会各个字符对应的 j 的新位置存储到一个数组中,并给这个数组起名为 Next 数组,数组中的值统称为 next 值。


模式串中各个字符对应的 next 值的计算方式是,取该字符前面的字符串(不包含自己),其前缀字符串和后缀字符串相同字符的个数再 +1 就是该字符对应的 next 值。

前缀字符串指的是位于模式串起始位置的字符串,例如模式串 “ABCD”,则 “A”、“AB”、“ABC” 都属于前缀字符串;后缀字符串指的是位于串结尾处的字符串,还拿模式串 “ABCD” 来说,“D”、“CD”、“BCD” 为后缀字符串。

注意,模式串中第一个字符对应的值为 0,第二个字符对应的值是 1 ,这是固定不变的。因此模式串 “ABCAE” 中各个字符对应的 next 值如图

在这里插入图片描述
各个字符对应 next 值的计算过程是:

  • 第三个字符 ‘C’:在前面的字符串 “AB” 中,前缀字符串和后缀字符串相等个数为 0,0 + 1 = 1,所以字符 ‘C’ 对应的 next 值为 1。
  • 第四个字符 ‘A’:在前面的字符串 “ABC” 中,前缀字符串和后缀字符串相等个数为 0,0 + 1 = 1,所以字符 ‘A’ 对应的 next 值为 1。
  • 第五个字符 ‘E’:在前面的字符串 “ABCA” 中,前缀字符串和后缀字符串相等个数为 1,1 + 1 = 2,所以字符 ‘E’ 对应的 next 值为 2。

当字符 ‘E’ 匹配失败时,指针 j 指向模式串数组中第 2 个字符,即 ‘B’


那么,如果编写程序计算出模式串对应的 NEXT 数组呢?

可以设计这样一个算法:刚开始时令 j 指向模式串中第 1 个字符(j=1),i 指向第 2 个字符(i=2)。接下来,对每个字符做同样的操作:

  • 如果 i 和 j 指向的字符相等,则 i 后面第一个字符的 next 值为 j+1,同时 i 和 j 做自加 1 操作,为求下一个字符的next 值做准备;
  • 如果 i 和 j 指向的字符不相等,则执行j=next[j]修改 j 的指向,然后以同样的方法对比 i 和 j 指向的字符,以此类推。当 j 的值为 0 时,将 i 后面第一个字符的 next 值置为 1。

例如,计算模式串 “aaacd” 对应的 NEXT 数组,实现过程为:

  • 前两个字符对应的 next 值分别为 0 和 1(j=1, i=2);

  • 由于 i 和 j 指向的字符相等,所以第三个字符 ‘a’ 的 next 值为 j +1 = 2,同时 i 和 j 各自加 1(此时 j=2,i=3)。

在这里插入图片描述

  • 由于 i 和 j 指向的字符相等,所以第四个字符 ‘c’ 的 next 值为 j +1 = 3,同时 i 和 j 各自加 1(此时 j=3,i=4)。
    在这里插入图片描述
  • 此时 i 和 j 指向的字符不相等,执行 j = next[j] 修改 j 的指向
    在这里插入图片描述
  • 从上图可以看到,i 和 j 指向的字符又不相同,继续执行 j = next[j]
    在这里插入图片描述

由于 j 和 i 指向的字符仍不相等,继续执行 j=next[j] 得到 j=0,字符 ‘d’ 对应的 next 值为 1。

实际上,当第一次比较 i 和 j 不相等时,意味着匹配失败位置前的最长前缀和后缀字符串不相同;执行 j=next[j] 后,i 和 j 仍不相等,意味着匹配失败位置前的次长前缀和后缀字符串也不相同,以此类推。当 j = 0 时,意味着匹配失败位置前没有相等的前缀和后缀字符串。

这里给出上述思想实现 NEXT 数组的 C 语言代码:

void GetNext(SString string,int* next){//string是字串
    int i = 1,j = 0;
    //next的第一个元素固定为0
    next[i] = 0;
    while (i < string.length)
    {
        if (j==0||string.data[i]==string.data[j])
        {
            ++i;++j;
            next[i] = j;//Pi=Pj,则next[J+1]=next[J]+1
        }else{
            j = next[j];
        }
    }
}

假设主串 A 为 “ababcabcacbab”,模式串 B 为 “abcac”,KMP 算法进行模式匹配的过程为:

  • 第一次匹配如图所示,匹配结果失败,指针 j 移动至 next[j] 的位置;

在这里插入图片描述

  • 第二次匹配如图所示,匹配结果失败,执行 j=next[j] 操作

在这里插入图片描述

  • 第三次匹配成功

在这里插入图片描述
使用 KMP 算法只需匹配 3 次,而同样的问题使用 BF 算法则需匹配 6 次才能完成。

KMP 算法:

int KMP(SString s,SString t,int* next){
    int i = 1,j = 1;
    while (i<=s.length&&j<=t.length)
    {
        if (s.data[i]==t.data[j])
        {
            ++i;++j;
        }else{
            j = next[j];
        }  
    }
    if (j>t.length)
    {
        return i - t.length;
    }else{
        return 0;
    }
}

KMP算法的进一步优化

只需要更改next即可

void GetNextVal(SString string,int* next){//string是字串
    int i = 1,j = 0;
    //next的第一个元素固定为0
    next[i] = 0;
    while (i < string.length)
    {
        if (j==0||string.data[i]==string.data[j])
        {
            ++i;++j;
            if (string.data[j]!=string.data[i])
            {
                next[i] = j;//Pi=Pj,则next[J+1]=next[J]+1
            }else{
                next[i] = next[j];
            }
        }else{
            j = next[j];
        }
    }
}

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

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

相关文章

【C++】构造函数分类 ③ ( 调用有参构造函数的方法 | 括号法 | 等号法 )

文章目录 一、在不同的内存中创建类的实例对象1、括号法调用构造函数2、等号法调用构造函数 二、完整代码示例 一、在不同的内存中创建类的实例对象 在上一篇博客 【C】构造函数分类 ② ( 在不同的内存中创建类的实例对象 | 栈内存中创建实例对象 | new 关键字创建对象 ) 中 , …

从0到1学会Git(第三部分):Git的远程仓库链接与操作

写在前面:前面两篇文章我们已经学会了git如何在本地进行使用&#xff0c;这篇文章将讲解如何将本地的git仓库和云端的远程仓库链接起来并使用 为什么要使用远程仓库:因为我们需要拷贝我们的代码给别人以及进行协同开发&#xff0c;就需要有一个云端仓库进行代码的存储和同步&a…

nginx配置-gzip

1、想看nginx配置的时候&#xff0c;发现没有nginx命令&#xff0c;是没有配置环境变量。 cd etc/ vim profile 加入 unset i unset -f pathmunge PATH/usr/local/nginx/sbin:$JAVA_HOME/bin:$PATH //这一行 export JAVA_HOME/usr/local/soft/jdk8 expo…

qpushbutton 样式表

QPushButton {color:#ffffff; /*文字颜色*/background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop:0 #aa55ff, stop: 1 #1296db);/*背景色*/border-style:outset; /*边框风格*/border-width:2px;/*边框宽度*/border-color:#0055ff; /*边框颜色*/border-radius:10…

如何用 Java 找到字符串中的元音

这个题目其实不难&#xff0c;这是一个公司面试的时候要求的题目。 这个公司的面试有点意思&#xff0c;他们希望 Zoom 看我的电脑&#xff0c;然后让我解决问题。 题目 题目就非常简单了&#xff0c;他们给了我 2 个字符串。 其中一个是测试字符串&#xff0c;另外一个是元…

I2C 验证中需要注意的问题

I2C验证中需要注意的问题 1.NACK出现的时刻2.Restart和Start区别3.保持时间&#xff08;Thd&#xff09; 1.NACK出现的时刻 通常&#xff0c;在Master访问到错误的Device ID时&#xff0c;Slave会返回Nack&#xff0c;表示访问失败。此外&#xff0c;在Master对Slave读访问时&…

JWT认证、drf-jwt安装和简单使用、实战之使用Django auth的User表自动签发、实战之自定义User表,手动签发

一 JWT认证 在用户注册或登录后&#xff0c;我们想记录用户的登录状态&#xff0c;或者为用户创建身份认证的凭证。 我们不再使用Session认证机制&#xff0c;而使用Json Web Token&#xff08;本质就是token&#xff09;认证机制。Json web token (JWT), 是为了在网络应用环境…

沙丁鱼优化算法(Sardine optimization algorithm,SOA)求解23个函数MATLAB

一、沙丁鱼优化算法 沙丁鱼优化算法(Sardine optimization algorithm,SOA)由Zhang HongGuang等人于2023年提出&#xff0c;该算法模拟沙丁鱼的生存策略&#xff0c;具有搜索能力强&#xff0c;求解精度高等特点。 沙丁鱼主要以浮游生物为食&#xff0c;这些生物包括细菌、腔肠…

性能测试 —— Jmeter定时器

固定定时器 如果你需要让每个线程在请求之前按相同的指定时间停顿&#xff0c;那么可以使用这个定时器&#xff1b;需要注意的是&#xff0c;固定定时器的延时不会计入单个sampler的响应时间&#xff0c;但会计入事务控制器的时间 1、使用固定定时器位置在http请求中&#xf…

5.9.Webrtc线程事件处理

在前面的课程中呢&#xff0c;我已经向你介绍了事件处理的一些基础知识&#xff0c;那今天呢&#xff0c;我们再来看一下外边儿rtc下事件处理的基本逻辑是什么&#xff1f; 那首先呢&#xff0c;我们来看一下事件是如何协调线程工作的&#xff0c;那就如果这张图所展示的有两个…

一起学数据结构(6)——栈和队列

上篇文章中&#xff0c;对栈的概念及特点进行了解释&#xff0c;并且给出了栈实现的具体代码。本篇文章将给出队列的基本概念及特点。并给出相应的代码。 1. 队列的概念及结构&#xff1a; 在给出队列的概念之前&#xff0c;先给出上篇文章中提到的栈的概念&#xff1a;一种只…

JVM系列 运行时数据区

系列文章目录 第一章 运行区实验 文章目录 系列文章目录前言一、堆&#xff08;Heap&#xff09;1.1、新生代/Young区1.1.1、Eden区1.1.2、Survival区 1.2、年老代&#xff08;old区&#xff09; 二、虚拟机栈&#xff08;Stack&#xff09;2.1、栈顶缓存技术2.2、溢出2.3、栈…

约瑟夫环(循环列表实现)

约瑟夫&#xff08;Joseph&#xff09;问题的一种描述是&#xff1a;编号为1&#xff0c;2&#xff0c;3&#xff0c;…&#xff0c;n的n个人按顺时针方向围坐一圈。每人持有一个密码&#xff08;正整数&#xff09;。一开始任选一个正整数作为报数上限值m&#xff0c;从第一个…

vscode c++解决包含头文件红色波浪线问题

安装c/c插件后&#xff0c;按ctrlshiftp&#xff0c; 点击打开了c_cpp_properties.json文件&#xff0c;对其中的IncludePath进行编辑&#xff0c;示例如下&#xff1a; "includePath": ["${workspaceFolder}/**","${workspaceFolder}/include/**&q…

FastChat

Fast Chat是一个用于训练/部署和评估基于大型语言模型的聊天机器人的开发平台。其核心功能包括&#xff1a; 最先进模型的权重/训练代码和评估代码(例如Vicuna/FastChat-T5)基于分布式多模型的服务系统&#xff0c;具有Web界面和与OpenAI兼容的RESTful API。 安装 pip instal…

Dajngo01_Django框架基础与环境搭建

Dajngo01_Django框架基础与环境搭建 在正式开始学习Django之前&#xff0c;我们先简单了解一下Web应用程序的本质&#xff1a; 接收并解析HTTP请求&#xff0c;获取具体的请求信息处理本次HTTP请求&#xff0c;即完成本次请求的业务逻辑处理构造并返回处理结果——HTTP响应 那么…

Redis 数据一致性方案的分析与研究

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 一般的业务场景都是读多写少的&#xff0c;当客户端的请求太多&#xff0c;对数据库的压力越来越大&#xff0c;引入缓存来降低数据库的压力是必然选择&#xff0c;目前业内…

【移动开发】最新uniapp开发的多端影视APP+后端对接的苹果CMS源码(可打包多端app)

介绍&#xff1a; 分享一款uniapp开发的多端影视APP源码&#xff0c;后端对接的是苹果10CMS。 这个APP有缓存、投屏、回放记录、收藏等功能&#xff0c;非常完善。 会员中心也使用苹果cms的数据。 可以打包到多个端的代码。 本APP前端用的UI框架是特漂亮的colorUI&#xff0…

当我们谈外包,我们在谈什么?

大家好&#xff0c;我是苍何&#xff0c;周末发小来找我玩耍&#xff0c;聊到了他当初在华为外包的经历&#xff0c;感触挺大的&#xff0c;给大家做下分享。 小 Z 大学专科材料专业毕业&#xff0c;毕业后报名了 IT 培训班&#xff0c;学习了一年&#xff0c;之后面试去了华为…

关于安卓grovvy开发(一)bugly混淆自动上报

背景 打包&#xff0c;mapping&#xff0c;上传&#xff0c;这些是不是都困扰了开发很多。苦于复制粘贴&#xff1f;反正我是。于是&#xff0c;有了这个 最终效果 最终的效果&#xff0c;就是点击项目的打包gradle&#xff0c;复制mapping文件且进行bugly混淆的自动上报。 …