【理解串】

目录

  • 一、串的基本概念
  • 二、串的基本操作及实现
  • 三、串的存储实现
    • 3.1、静态数组实现
    • 3.2、动态数组实现
  • 四、串的朴素模式匹配
    • 4.1、算法思想
    • 4.2、代码实现
  • 五、KMP算法
    • 5.1、算法思想
    • 5.2、求模式串的next数组
    • 5.2、代码实现

一、串的基本概念

串:即字符串(string),是由零个或多个字符组成的有限序列,一般记为S = “a1a2a3a4...an”(n>=0)。其中,S是串名,双引号’'括起来的字符序列是串的值,a可以是字母、数字或其他字符,串中字符的个数n称为串的长度,当n=0时,称串为空串。
注意:单引号里只能放一个字符,双引号中可以放字符串,两个占用空间也有区别,示例:

#include<iostream>

using namespace std;

int main() {
    cout << "\'s\'的长度为:" << sizeof('s') << endl;
    cout << "\"s\"的长度为:" << sizeof("s") << endl;
    return 0;
}

运行结果如下:
在这里插入图片描述
因为"a"字符串结尾有一个’\0’字符,表示字符串结束,它也会占一个字节,而字符’a’只占一个字节。

子串:串中任意连续的字符组成的子序列;
主串:包含子串的串。

字符在主串中的位置:字符在串中的序号;
子串在主串中的位置:子串的首字符在主串中的位置。

串是一种特殊的线性表,数据元素之间呈线性关系,串的数据对象限定为字符集(中文字符、英文字符、数字字符、标点字符等)。

二、串的基本操作及实现

#include<iostream>
#define MaxSize 100

using namespace std;


//定长字符串
struct staiticString{
    char str[MaxSize + 1];//为什么要+1
    int length;//字符串长度
};

struct variableString{
    char* str;
    int length;
};

//字符串的基本操作
//串赋值
bool stringAssign(variableString& S, char* ch) {
    delete S.str;

    int length = 0;
    char* c = ch;

    while (*c != '\0')
    {
        length++;
        c++;
    }

    if (length == 0) {
        S.str = nullptr;
        S.length = 0;
        return true;
    }
    else {
        S.str = new char[length + 1];
        if (S.str == nullptr) {
            return false;
        }
        else {
            c = ch;
            for (int i = 0; i <= length; i++,++c) {
                S.str[i] = *c;
            }
            S.length = length;
            return true;
        }

    }
}

//获取字符串长度
int GetLength(variableString S) {
    return S.length;
}

//字符串比较
int stringCompare(variableString S1, variableString S2){
    for (int i = 0; i < S1.length && i < S2.length; ++i) {
        if (S1.str[i] != S2.str[i]) {
            return S1.str[i] - S2.str[i];
        }
    }
    //扫描过的所有字符都相同,则长度长的串更大
    return S1.length - S2.length;
}

//连接串,将串S1和串S2连接起来,并将连接结果返回到result
bool stringContact(variableString &result, variableString &S1, variableString S2) {
    delete result.str;
    result.str = nullptr;

    result.str = new char[S1.length + S2.length + 1];
    if (result.str == nullptr) {
        cerr << "Memory is not enough!" << endl;
        return false;
    }
    //将S1的内容先放到result里
    int i = 0;
    while (i<S1.length) {
        result.str[i] = S1.str[i];
        i++;
    }
    //再把S2的内容放到紧接着S1内容的后面
    int j = 0;
    while (j<S2.length) {
        result.str[i + j] = S2.str[j];
        j++;
    }
    result.length = S1.length + S2.length;

    return true;
}

//求子串,其中from为子串的起始位置,length为子串的长度
bool subString(variableString &reslut, variableString S1,int from, int length) {
    //判断给定参数的合法性
    if (from<0||from>S1.length||length<0||length>S1.length-from) {
        cerr << "Parameters are wrong" << endl;
        return false;
    }

    delete reslut.str;
    reslut.str = nullptr;

    if (length ==0) {
        reslut.str = nullptr;
        reslut.length = 0;
        return true;
    }
    else {
        reslut.str = new char[length + 1];
        int i = from;
        int j = 0;
        while (i<from+length)
        {
            reslut.str[j++] = S1.str[i++];
        }

        reslut.str[j] = '\0';
        reslut.length = length;
        return true;
    }

}

//清空串
bool clearString(variableString &S) {
    delete S.str;
    S.str = nullptr;
    S.length = 0;
    return true;
}

int main() {
    variableString S1{};
    char ch[MaxSize] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!', '\0'};
    //调用串赋值
    stringAssign(S1,ch);
    for (int i = 0; i < S1.length + 1; ++i) {
        cout << S1.str[i];
    }
    cout << endl;

    variableString S2{};
    //S2.str = ch;
    //S2.length = S1.length;
    char ch1[MaxSize] = { 'h','e','l','l','o','\0'};
    stringAssign(S2,ch);

    //调用串比较
    if (stringCompare(S1, S2) == 0) {
        cout << "S1 equals S2" << endl;
    }else if(stringCompare(S1,S2)>0){
        cout << "S1 is bigger than S2" << endl;
    }
    else {
        cout << "S1 is smaller than S2" << endl;
    }

    //串连接
    variableString result1{};
    stringContact(result1,S1,S2);
    while (*result1.str != '\0')
    {
        cout << *result1.str++;
    }
    cout << endl;

    //求子串
    variableString result2{};
    subString(result2,S1,1,8);
    while (*result2.str != 0)
    {
        cout << *result2.str++;
    }
    cout << endl;
    if (clearString(S1)) {
        cout << "S1 has been cleared!" << endl;
    }

    return 0;
}

三、串的存储实现

3.1、静态数组实现

#define MaxSize 100

typedef struct staticString{
     char ch[MaxSize];
     int length;
};

3.2、动态数组实现

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

四、串的朴素模式匹配

串的模式匹配:
在主串中找到与模式串相同的子串,并返回其所在主串中的位置。

4.1、算法思想

  1. 先将主串中与模式串长度相同的子串找出来,挨个与模式串对比,当所比子串与模式串某个对应字符不匹配时,就立即放弃当前子串,转而检索下一个子串;
  2. 若模式串长度为m,主串长度为n,则直到匹配成功/匹配失败最多需要进行(n-m+1)*m次,最坏时间复杂度为:O(mn);
  3. 最坏情况:每个子串的前m-1个字符都与模式串匹配,只有第m个字符不匹配;
  4. 比较好的情况:每个子串的第1个字符就与模式串匹配。

4.2、代码实现

//S为主串,cs为模式串(子串)
int Index(string S, string cs){
    int k = 1;
    int i = k, j = 1;
    while(i<=S.length && j<= cs.length){
        if(S.str[i] == cs.str[j]){
            ++i, ++j;
        }else{
            k++,i=k,j+1;
        }
    }
    if(j>cs.length){
        return k;
    else
        return 0;
}

或者不用k的方法:

int Index(string S, string cs){
    int i=0;//扫描主串
    int j=0;//扫描模式串
    while(i<S.length && j<cs.length){
        if(S.str[i] == cs.str[i]){
            ++i;
            ++j;//继续比较后续字符
        }else{
        i = i-j + 1;//指针后退,重新开始匹配
        j = 1;
        }
    }
    if(j>cs.length)
    return i-cs.length;
    else return -1;
}

匹配成功的最好时间复杂度为:O(m);
匹配失败的最好时间复杂度为:O(n);
最坏时间复杂度为:O(mn);

五、KMP算法

5.1、算法思想

朴素模式串匹配算法的缺点:当某些子串与模式串部分匹配时,主串的扫描指针i经常回溯,导致时间开销增加;
KMP算法:当子串和模式不匹配时,主串指针不回溯,模式串指针j=next[j],算法平均时间复杂度:O(m+n)。

5.2、求模式串的next数组

  1. 串的前缀:包含第一个字符,且不包含最后一个字符的子串;
  2. 串的后缀:包含最后一个字符,且不包含第一个字符的子串;
  3. 当第i个字符匹配失败时,由前1~j-i个字符组成的串记为s,next[i]=s的最长前后缀长度+1,特别地:规定next[1]=0;

5.2、代码实现

//获取next数组
void getNext(SString SS, int next[]){
    int i=1, j=0;
    next[1]=0;
    while(i<SS.length){
        if(j==0||SS.str[1]==SS.str[j]{
            ++i,++j;
            next[i]=j;
        }else{
            j=next[j];
        }
    }
}

//KMP算法,求主串中模式串的位序,没有则返回0
int Index_KMP(string S, string cs){
    int i=1,j=1;
    int next[cs.length+1];
    getNext(cs,next);
    while(i<=S.length || j<=cs.length){
        if(j==0 || S.str[i] == cs.str[j]){
            ++i,++j;
        }else{
            j=next[j];
        }
    }
    if(j>cs.length)
        return i-cs.length;
     else return 0;
}

int main(){
    SString S={"ababcabcd", 9};
	SString T={"bcd", 3};
	printf("%d ", Index_KPM(S, T));	//输出9
}

KMP算法的进一步优化,改进next数组:

void getNextval(SString T, int nextval[]){
    int i=1,j=0;
    nextval[1]=0;
    while(i<T.length){
        if(j==0 || T.ch[i]==T.ch[j]){
            ++i; ++j;
            if(T.ch[i]!=T.ch[j])
                nextval[i]=j;
            else
                nextval[i]=nextval[j];
        }else
            j=nextval[j];
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一行命令快速导出、导入Python的依赖环境(Python)

文章目录 一、pip1、导出2、导入 二、Conda&#xff08;简&#xff09;1、导出1、导入 一、pip 1、导出 在Pycharm的Terminal窗口输入如下命令&#xff0c;即可将环境导出至文件requirements.txt。 pip freeze > C:\Users\sdl\Deskto\requirements.txt也可以在DOS界面执行…

Python 核心编程

Python 核心编程 1. 数据类型1.1 整型 int1.2 浮点数 float1.3 布尔类型 bool1.4 字符串 str1.5 列表 list1.6 元组 tuple1.7 集合 set1.8 字典 dict 2. 逻辑结构、文件操作2.1 分支结构和三元表达2.2 循环和遍历2.3 目录和路径2.4 文件操作 3. 函数、类、异常处理3.1 函数3.2 …

[Flask笔记]一个完整的Flask程序

前面讲过Flask是一个轻量级Web开发框架&#xff0c;为什么说是轻量级的呢&#xff0c;因为它用短短几行代码就能运行起来&#xff0c;我们一起来看看最简单的flask框架。 安装Flask 在看Flask框架之前我们需要先安装flask模块&#xff0c;学过python的肯定都知道&#xff0c;…

2.5 计算机网络

声明&#xff1a;文章参考的《系统架构设计师教程&#xff08;第二版&#xff09;》&#xff0c;如有侵权&#xff0c;本人将立即修改和删除。 利用通信线路将地理上分散的、具有独立功能的计算机系统和通信设备按不同的形式连接起来&#xff0c;并依靠网络软件以及通信协议实现…

《昇思25天学习打卡营第18天|onereal》

RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This film…

Android数据库基础

目录 1、安卓数据存储方式 2、数据库事务 数据库事务的特性(ACID) 事务的隔离级别 事务总结 3、ContetProvider 作用 ​编辑 统一资源标识符URI ​编辑 MIME类型 ContentProvider主要方法 4、ContentResolver 作用 主要方法 使用案例 辅助工具类 ContentUris Uri…

matlab 有倾斜的椭圆函数图像绘制

matlab 有倾斜的椭圆函数图像绘制 有倾斜的椭圆函数图像绘制xy交叉项引入斜线负向斜线成分正向斜线成分 x^2 y^2 xy 1 &#xff08;负向&#xff09;绘制结果 x^2 y^2 - xy 1 &#xff08;正向&#xff09;绘制结果 有倾斜的椭圆函数图像绘制 为了确定椭圆的长轴和短轴的…

torchplus

https://gitee.com/hj_research/torchplus 一、安装 pip install tplus

Linux磁盘-创建分区

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux磁盘涉及到的命令不是很多&#xff0c;但是在实际运维中的作用却很大&#xff0c;因为Linux系统及业务都会承载到硬盘…

评估指标:精确率(Precision)、召回率(Recall)、F1分数(F1 Score)

评估指标&#xff1a;精确率&#xff08;Precision&#xff09;、召回率&#xff08;Recall&#xff09;、F1分数&#xff08;F1 Score&#xff09; 前言相关介绍1. 准确率&#xff08;Accuracy&#xff09;2. 精确率&#xff08;Precision&#xff09;3. 召回率&#xff08;Re…

君子签电子合同推动企业人事管理变革,降本提效

在日益复杂的人力资源管理领域&#xff0c;合同签署与管理成为HR面临的一大挑战。面对庞大的合同量、繁琐的审批流程、频繁的岗位变动以及离职时的合同管理难题&#xff0c;传统方式已难以满足高效、安全、合规的需求。 君子签针对HR面临的挑战和需求&#xff0c;打造智能合同…

Alertmanager告警配置

1、告警概述及说明 告警能力在Prometheus的架构中被划分成两个独立的部分。 通过在Prometheus中定义AlertRule(告警规则)&#xff0c;Prometheus会周期性的对告警规则进行计算&#xff0c;如果满足告警触发条件就会向Alertmanager发送告警信息。 当Alertmanager接收到 Promethe…

java项目如何配置不同环境变量 以及 原理

如何配置不同的profile 首先&#xff0c;一个java项目&#xff0c;需要有不同的环境配置&#xff0c;打包时&#xff0c;自动使用对应的配置。那么&#xff0c;如何实现呢&#xff1f; 在你的Spring Boot项目的src/main/resources目录下创建或添加一个application.yml文件。这…

2024前端面试题之Vue3

2024前端面试题之Vue3 在面试具有五年经验的前端工程师时&#xff0c;对于 Vue 3 的掌握程度是一个重要的考核点。本文将提供一系列针对这一级别工程师的 Vue 3 面试题&#xff0c;并附上详细的解析&#xff0c;帮助面试官全面评估候选人的技术实力和项目经验。 一、Vue 3 基础…

科普文:微服务之服务网格Service Mesh

一、ServiceMesh概念 背景 随着业务的发展&#xff0c;传统单体应用的问题越来越严重&#xff1a; 单体应用代码库庞大&#xff0c;不易于理解和修改持续部署困难&#xff0c;由于单体应用各组件间依赖性强&#xff0c;只要其中任何一个组件发生更改&#xff0c;将重新部署整…

shell脚本之if/case语句

一、条件测试 1、1 返回码 $? $? :返回码&#xff0c;用来判断命令或者脚本是否执行成功。 0 &#xff1a;表示true &#xff0c;成功&#xff1b;非0 则表示flase &#xff0c;失败。 1、2 test命令 可以进行条件测试&#xff0c;然后根据返回值来判断条件是否成立 -e…

第2关 Python 基础知识

第2关 Python 基础知识 前言Python实现wordcountSSH远程连接开发机Bug记录 前言 本文是由上海人工智能实验室主办的第三期书生大模型实战营的笔记&#xff0c;仅供个人和助教批改作业参考&#xff0c;教程原文链接。 报名请在微信搜索“第三期书生大模型实战营”。 本笔记是在…

BatchNorm LayerNorm

0. Abstract 很早以前就遇到了 BatchNorm 和 LayerNorm, 当时只是粗略地知道它们是对数据进行了标准化: x x − μ σ \bm{x} \frac{\bm{x} - \bm{\mu}}{\bm{\sigma}} xσx−μ​ 这当然很简单, 但实际的数据是比较复杂的. 对于 CV 任务的数据 image 而言, 一个 batch 的数…

linux系统操作/基本命令/vim/权限修改/用户建立

Linux的目录结构&#xff1a; 一&#xff1a;在Linux系统中&#xff0c;路径之间的层级关系&#xff0c;使用:/来表示 注意:1、开头的/表示根目录 2、后面的/表示层级关系 二&#xff1a;在windows系统中&#xff0c;路径之间的层级关系&#xff0c;使用:\来表示 注意:1、D:表示…

【技术追踪】HiDiff:医学图像分割的混合扩散框架(TMI-2024)

传统分割方法与扩散分割方法结合&#xff0c;做大做强~ HiDiff&#xff1a;一种用于医学图像分割的新型混合扩散框架&#xff0c;它可以协同现有判别分割模型和新型生成扩散模型的优势&#xff0c;在腹部器官、脑肿瘤、息肉和视网膜血管分割数据集上性能表现 SOTA &#xff01;…