【理解串】

news2024/9/9 8:03:00

目录

  • 一、串的基本概念
  • 二、串的基本操作及实现
  • 三、串的存储实现
    • 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];
    }
}

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

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

相关文章

常见的点云数据的获取方式

1. 激光雷达&#xff08;LiDAR&#xff09; 获取方式&#xff1a;激光脉冲测距原理&#xff1a;激光雷达通过发射激光脉冲并接收反射信号来测量物体与传感器之间的距离。计算激光脉冲从发射到返回所需的时间&#xff0c;并将其转换为距离&#xff0c;从而生成三维点云数据。常…

上班摸鱼吗?一文详解代码生成神器-Velocity

引言 “我不是在教你学坏,而是教你如何提高生产效率。” ----------- 牛顿 人类社会能够一直进步发展出现在的文明世界,最大的一个原因就是这个世界上懒人居多,懒人为了偷懒就需要提高生产效率,效率提高节省下来的时间才能创造出艺术、娱乐以及更高效率的科学技术。程序员…

丑数问题,力扣264,坑点

丑数问题&#xff0c;力扣264&#xff0c;坑点 力扣链接 给你一个整数 n &#xff0c;请你找出并返回第 n 个 丑数 。 丑数 就是质因子只包含 2、3 和 5 的正整数。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;12 解释&#xff1a;[1, 2, 3, 4, 5, 6, 8, 9, …

33 IRF配置思路

IRF配置思路网络括谱图 主 Ten-GigabitEthernet 1/0/49 Ten-GigabitEthernet 1/0/50 Ten-GigabitEthernet 1/0/51 备 Ten-GigabitEthernet 2/0/49 Ten-GigabitEthernet 2/0/50 Ten-GigabitEthernet 2/0/51 思路 主 1 利用console线进入设备的命令行页面去更改…

Spark源码详解

https://www.cnblogs.com/huanghanyu/p/12989067.html#_label3_3

服了,jenkins找不到advanced

新手下载的最新版本&#xff0c;过新手入门的时候一直过不去&#xff0c;就跳过了。 想下载一个汉化&#xff0c;还下载不了。根据提示搜索&#xff0c;结果大家让去advanced找url&#xff0c;也找不到。

[C++] 模拟实现list(二)

标题&#xff1a;[C] 模拟实现list&#xff08;二&#xff09; 水墨不写bug 目录 &#xff08;一&#xff09;回顾 &#xff08;二&#xff09;迭代器类的封装设计 &#xff08;1&#xff09;成员函数简要分析 &#xff08;2&#xff09;const迭代器类的设计 &#xff08;…

国漫推荐08

仙侠、武侠、恋爱、战斗、现代、古风 1.《仙王的日常生活》仙侠、日常、搞笑 《仙王的日常生活》第一季 《仙王的日常生活 第二季》 《仙王的日常生活 第三季》 《仙王的日常生活 第四季》 2.《风灵玉秀》武侠、少女 3.刺客伍六七 番名季度上映时间《伍六七》第一季2018-04-…

ppt如何翻译最高效?盘点5个便捷易用的ppt翻译器

正值夏日炎炎&#xff0c;7.15初伏即将到来&#xff0c;天气更是开始热到让人无法专心工作~面对电脑上一堆非母语的PPT文件&#xff0c;看得人愈发烦躁。 幸运的是&#xff0c;我手里头常常备着几款ppt翻译工具&#xff0c;这才让办公显得没那么枯燥和烦闷~倘若你也遇上同样的…

技术速递|宣布为 .NET 升级助手提供第三方 API 和包映射支持

作者&#xff1a;Marco Goertz 排版&#xff1a;Alan Wang .NET 升级助手是一个 Visual Studio 扩展和命令行工具&#xff0c;可帮助您将应用从之前的 .NET 和 .NET Framework 升级到最新版本的 .NET。正如我们在之前的文章中所描述的那样&#xff0c;它为升级 Microsoft 库和框…

vite+vue3创建cesium (ts/js)

在要创建项目的文件夹。输入cmd 1.搭建第一个Vite项目。 npm init vitelatest 安装Cesium插件 cesium插件&#xff1a;vite-plugin-cesium npm i cesium vite-plugin-cesium vite -D配置cesium 在vite.config.ts/vite.config.js文件中 import cesium from vite-plugin-ces…

韦东山嵌入式linux系列-LED 驱动程序框架

1 回顾字符设备驱动程序框架 图中驱动层访问硬件外设寄存器依靠的是 ioremap 函数去映射到寄存器地址&#xff0c;然后开始控制寄存器。 那么该如何编写驱动程序&#xff1f; ① 确定主设备号&#xff0c;也可以让内核分配&#xff1b;② 定义自己的 file_operations 结构体&…

【D3.js in Action 3 精译】1.3 D3 视角下的数据可视化最佳实践(下)

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介 ✔️ 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知 1.2.1 HTML 与 DOM1.2.2 SVG - 可缩放矢量图形1.2.3 Canvas 与 WebGL1.2.4 CSS1.2.5 JavaScript1.2.6 Node 与 JavaScript 框架1.2.7 Observable 记事…

Qt常用基础控件总结—旋转框部件(QSpinBox类和QDoubleSpinBox类)

旋转框(微调按钮)部件 QAbstractSpinBox 类 QAbstractSpinBox 类介绍 QAbstractSpinBox 类是 QWidget 类的直接子类,虽然该类不是抽象类,但该类并未提供实际的功能,仅为旋转框提供了一些外观的形式以及需要子类实现了成员,也就是说点击微调按钮的上/下按钮,不会使其中的…

DID差分模型案例集(传统DID、队列DID、渐近DID、空间DID、PSM-DID)

双重差分&#xff08;DID&#xff09;模型是一种广泛应用于经济学、社会学等领域的统计方法&#xff0c;主要用于评估政策或事件的因果效应。以下是DID模型几个重要变体的简要介绍&#xff1a; 1、传统DID&#xff08;Traditional DID&#xff09;&#xff1a;这是DID模型的基…

Keepalived+HAProxy 集群及虚IP切换实践

1、软件介绍 ①Keepalived keepalive是一个用c语言编写的路由软件&#xff0c;这个项目的主要目标是为Linux系统和基于Linux的基础设施提供简单而健壮的负载平衡和高可用性设施。负载均衡框架依赖于众所周知且广泛使用的Linux Virtual Server (IPVS)内核模块提供第4层负载均衡…

代码随想录算法训练营Day21 | 669. 修剪二叉搜索树 | 108.将有序数组转换为二叉搜索树 | 538.把二叉搜索树转换为累加树

今日任务 669. 修剪二叉搜索树 题目链接&#xff1a; https://leetcode.cn/problems/trim-a-binary-search-tree/description/题目描述&#xff1a; Code class Solution { public:TreeNode* trimBST(TreeNode* root, int low, int high) {if(root nullptr){return root;…

应力 (Stress) 是指单位面积上所承受的力

应力 (Stress) 是指单位面积上所承受的力 flyfish 轴向力 轴向力 (Axial Force) 是指沿着物体的纵轴施加的力。对于一根杆或柱子&#xff0c;轴向力可以是拉力或压力&#xff0c;具体取决于力的方向。 拉力 (Tensile Force)&#xff1a;使物体拉长的力。 压力 (Compressive…

程序员学长 | 快速学习一个算法,GAN

本文来源公众号“程序员学长”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;快速学习一个算法&#xff0c;GAN GAN 如何工作&#xff1f; GAN 由两个部分组成&#xff1a;生成器&#xff08;Generator&#xff09;和判别器&…

C标准库读写文件

函数介绍 库变量 变量描述size_t无符号整数类型&#xff0c;是sizeof关键字的结果&#xff0c;表示对象大小FILE文件流类型&#xff0c;适合存储文件流信息的对象类型 库宏 宏描述NULL空指针常量EOF表示已经到达文件结束的负整数stderr、stdin、stdout指向FILE类型的指针&a…