【C语言 数据结构】串 - 顺序

news2024/9/20 1:10:59

文章目录

  • 串 - 顺序
    • 一、串的表示及实现
      • 1.1 串的概念
      • 1.2 串的初始化
      • 1.3 复制串
      • 1.4 串的判空
      • 1.5 串的比较
      • 1.6 串的长度
      • 1.7 清空串
      • 1.8 串的拼接
      • 1.9 串的截取
      • 1.10 插入子串
      • 1.11 串的打印
    • 二、串的应用
      • 2.1 串的模式匹配 - 基本操作
      • 2.2 串的模式匹配 - BF
      • 2.3 串的模式匹配 - KMP
        • next数组
      • 测试


串 - 顺序

一、串的表示及实现

1.1 串的概念

在这里插入图片描述

(1)串(String):是零个或多个字符组成的有限序列。一般记为: S='a1a2…an' (n≥0)

  • 其中S为串名,用单引号括起来的为串值, n为串的长度。

(2)子串:串中任意个连续的字符组成的子序列称为该串的子串。

(3)主串:包含子串的串相应地称为主串。

  • 子串在主串中的位置通常将字符在串中的序号称为该字符在串中的位置。子串在主串中的位置则以子串的第一个字符在主串中的位置来表示。

(5)空格串:由一个或多个称为空格的特殊字符组成的串,其长度为串中空格字符的个数。

(6)空串:无任何字符组成的串,其串长度为零。

(7)串相等:只有当两个串的长度相等,并且每个对应位置的字符都相等时才相等。

串的基本操作:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<stdbool.h>

// 动态存储串
typedef struct {
    char *str;  // 基址
    int maxLength;
    int length; // 串的大小
} DString;

// 初始化操作
void StrAssign(DString *S, int max, char *string);

// 复制串
void StrCopy(DString *S, DString T);

// 串的判空
bool StrEmpty(DString S);

// 串比较
int StrCompare(DString S, DString T);

// 串的长度
int StrLength(DString S);

// 清空串
void ClearString(DString *S);

// 串的拼接
void StrConcact(DString *N, DString S, DString T);

// 串的截取
void StrSub(DString* sub, DString S, int pos, int len);

// 串的匹配
int StrIndex(DString S, DString T, int pos);
int Index(DString S, DString T, int pos);

// 插入子串操作
void StrInsert(DString *S, int pos, DString T);

// 串的打印
void StrPrint(DString *S);

返回顶部


1.2 串的初始化

/* 初始化操作
 * DString *S   串
 * int max      串的最大长度
 * char *string 待生成的串
 */
void StrAssign(DString *S, int max, char *string) {
    assert(S);  // 断言
    int i, m;
    S->length = strlen(string);  // 获取串的长度
    // 获取串的允许最大长度
    if (S->length > max) {
        m = S->length;
    } else {
        m = max;
    }
    S->maxLength = m;
    S->str = (char *) malloc(sizeof(char) * (m)); // 分配串的存储空间
    if (S->str == NULL) exit(-1);
    // 创建串
    for (i = 0; i < S->length; i++) {
        S->str[i] = string[i];
    }
}

返回顶部


1.3 复制串

/* 复制串
 *  DString* S  新串
 *  DString T   待复制的串
 */
void StrCopy(DString *S, DString T) {
    assert(S);  // 断言
    // 获取字串的长度
    S->length = T.length;
    // 分配串的存储空间
    S->str = (char *) malloc(sizeof(char) * (S->length));
    if (S->str == NULL) exit(-1);
    // 复制串
    for (int i = 0; i < S->length; i++) {
        S->str[i] = T.str[i];
    }
}

返回顶部


1.4 串的判空

/* 串的判空
 * DString S 待判串
 */
bool StrEmpty(DString S) {
    assert(&S); // 断言
    return S.length == 0;
}

返回顶部


1.5 串的比较

/* 串比较
 * DString S  串1
 * DString T  串2
*/
int StrCompare(DString S, DString T) {
    // 断言
    assert(&S);
    assert(&T);
    int i = 0;
    for (; i < S.length && i < T.length; i++) {
        if (S.str[i] != T.str[i]) {
            return S.str[i] - T.str[i];
        }
    }
    return S.length - T.length;
}

返回顶部


1.6 串的长度

/* 串的长度
 *   DString S 判长字串
 */
int StrLength(DString S) {
    assert(&S);
    return S.length;
}

返回顶部


1.7 清空串

/* 清空串
*  DString* S 待清空串
 */
void ClearString(DString *S) {
    assert(S);  // 断言
    free(S->str);  // 释放空间
    S->str = NULL; // 置空
    S->length = 0; // 置零
}

返回顶部


1.8 串的拼接

/* 串的拼接
 * DString *N  合并的新串
 * DString S  串1
 * DString T  串2
 */
void StrConcact(DString *N, DString S, DString T) {
    assert(&S);
    assert(&T);
    N->length = S.length + T.length; // 新串的长度
    N->str = (char *) malloc(sizeof(char) * (N->length));// 分配串的存储空间
    if (N->str == NULL) exit(-1);
    // 串1
    for (int i = 0; i < S.length; i++) {
        N->str[i] = S.str[i];
    }
    // 串2
    for (int i = 0; i < N->length; i++) {
        N->str[i + S.length] = T.str[i];
    }
}	

返回顶部


1.9 串的截取

/* 串的截取
 *  DString *sub  截取的新串
 *  DString S 被截取的母串
 *  int pos 截取子串的开始位置
 *  int len 截取子串的长度
 */
void StrSub(DString *sub, DString S, int pos, int len) {
    assert(&S); // 断言
    sub->length = len;  // 新串的长度
    sub->str = (char *) malloc(sizeof(char) * (sub->length + 1)); //分配串的存储空间
    if (sub->str == NULL) exit(-1);
    if (pos >= 0 && pos < S.length && len >= 0 && len <= S.length - pos + 1) {
        for (int i = 0; i < len; i++) { // 遍历
            sub->str[i] = S.str[pos + i];  // 从pos位置开始取字符
        }
    } else {
        printf("参数有问题,请核实!");
        exit(-1);
    }
}

返回顶部


1.10 插入子串

/* 插入子串操作
 * DString *S 串
 * int pos    插入子串的起始位置
 * DString T  待插入的串
 */
void StrInsert(DString *S, int pos, DString T) {
    assert(S);  // 断言
    int i;       // 变量i
    char *p;  // 新的基址
    if (pos < 0) {
        printf("参数pos出错!");
        exit(-1);
    } else {
        // 如果插入的串和原串的长度大于初始定义的最大串长
        if (S->length + T.length > S->maxLength) {
            // 重新分配空间
            p = (char *) realloc(S->str, (S->length + T.length) * sizeof(char));
        }
        // 原串从插入位置开始的串向后移动新串的长度个位置
        for (i = S->length - 1; i >= pos; i--) {
            S->str[i + T.length] = S->str[i];
        }
        // 子串插入
        for (i = 0; i < T.length; i++) {
            S->str[pos + i] = T.str[i];
        }
        // 新串的长度 = 原串 + 子串
        S->length = S->length + T.length;
    }
}

返回顶部


1.11 串的打印

/* 串的打印
 * DString *S 串
 */
void StrPrint(DString *S) {
    assert(S);  // 断言
    int i;
    for (i = 0; i < S->length; i++) {
        printf("%c", S->str[i]);
    }
    printf("\n");
}

返回顶部


二、串的应用

模式匹配:求子串(模式串)在主串(目标串)中的位置,若子串出现在主串中,则匹配成功,否则匹配不成功。常应用于文章中关键字的查找。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cgtBLS5z-1670080162200)(https://test1.jsdelivr.net/gh/Code-for-dream/Blogimages/img/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/image-20221125031103045.png)]

2.1 串的模式匹配 - 基本操作

/* 串的匹配
 * DString S 母串
 * DString T 字串
 * int pos 位置
 */
int StrIndex(DString S, DString T, int pos) {
    int n, m, i;
    DString sub;
    if (pos > 0) {
        n = StrLength(S);// 得到主串S的长度  17
        m = StrLength(T);// 得到子串T的长度   8
        i = pos;
        while (i <= n - m + 1) {  // 17-8+1 = 10
            StrSub(&sub, S, i, m);    // 取主串第i个位置,长度与T相等子串给sub
            StrPrint(&sub);
            if (StrCompare(sub, T) != 0) ++i;// 如果两串不相等
            else return i;  // 如果两串相等
        }
    }
    return 0;    // 若无子串与T相等,返回0
}

返回顶部


2.2 串的模式匹配 - BF

在这里插入图片描述

简单的模式匹配: i i i j j j 分别指向主串 S S S 中的字符 和 子串 T T T 中的字符,如果 S i = T j S_i=T_j Si=Tj i 4 i4 i4 j j j 分别加 1 1 1,指向下一个字符,否则, i i i 退回到本趟匹配起始位置的下一个位置, j j j 重新指向子串的第一个字符,开始下一趟匹配。

/*
 * BF 算法
 * DString S  母串
 * DString T  子串
 */
int Index(DString S, DString T, int pos) {
    if (pos < 0 || pos > S.length - T.length) {
        printf("pos参数值有误,请校验!");
        exit(-1);
    }
    int i = pos;               // i用于主串S中当前位置下标值
    int j = 0;                 // j用于子串T中当前位置下标值
    while (i < S.length && j < T.length) {  // 若i小于S的长度并且j小于T的长度时,循环继续
        if (S.str[i] == T.str[j]) {         // 两字母相等则继续
            i++;
            j++;
        } else {               // 指针后退重新开始匹配
            i = i - j + 1;     // i退回到上次匹配首位的下一位,即i=i-j+1
            j = 0;             // j退回到子串T的首位
        }
    }
    //子串所有字符都匹配完毕
    if (j >= T.length) {
        return (i - j); // 返回匹配的第一个字符的下标
    } else {
        return 0;      // 匹配失败
    }
}

缺点:存在回溯的问题!

返回顶部


2.3 串的模式匹配 - KMP

KMP算法核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数,从而达到快速匹配的目的。

KMP算法与BF算法(暴力算法)区别在于,主串 i i i 不会回退,并且模式串 j j j 不会每次都回到 0 位置。

image-20221125044725773

假设此时 i 指向 g,j 指向 c,匹配失败。

  • 此时 i 不进行回退,因为在这个地方前,两个字符串是有一部分相同的。
  • 我们观察,当我们保持 i 不动, j 退回到第三个位置也就是 c 时,不难发现两个字符串前 a b 是一样的。
  • 因此,我们需要借助 next 数组,帮助我们找到 j 需要回退的位置。

next数组

  • KMP算法的精髓就是next数组,next[j]=k,表示不同的 j 对应一个 k 值;k 表示模式串下标为 j 的元素,匹配失败时,要退回的位置。

  • 最长前后相等子串

    image-20221125032841992
  • next[j] = 匹配字符个数 + 1 参考视频:三分钟搞定 数据结构 串 KMP算法next数组求值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F0L8zo14-1670080162201)(https://test1.jsdelivr.net/gh/Code-for-dream/Blogimages/img/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/image-20221125043152387.png)]

/*
 * KPM
 */
void Next(char *T, int *next) {
    next[1] = 0;  // 初始化默认值
    int i = 1;
    int j = 0;
    while (i < strlen(T)) {
        if (j == 0 || T[i - 1] == T[j - 1]) {
            i++;
            j++;
            next[i] = j;
        } else {
            j = next[j];
        }
    }
}

int KMP(char *S, char *T) {
    int next[10];
    Next(T, next);//根据模式串T,初始化next数组
    int i = 1;
    int j = 1;
    while (i <= strlen(S) && j <= strlen(T)) {
        //j==0:代表模式串的第一个字符就和当前测试的字符不相等;
        // S[i-1]==T[j-1],如果对应位置字符相等,两种情况下,指向当前测试的两个指针下标i和j都向后移
        if (j == 0 || S[i - 1] == T[j - 1]) {
            i++;
            j++;
        } else {
            j = next[j];//如果测试的两个字符不相等,i不动,j变为当前测试字符串的next值
        }
    }
    if (j > strlen(T)) {//如果条件为真,说明匹配成功
        return i - (int) strlen(T);
    }
    return -1;
}

返回顶部


测试

#include "Strand.c"

void TestDString() {

    // 创建串S
    printf("*******************\n");
    printf("S串的创建:");
    DString S;
    StrAssign(&S, 5, "abc");
    // 打印串S
    StrPrint(&S); // abc

    // 新的串T
    printf("*******************\n");
    printf("S串的插入 => S+T:");
    DString T;
    StrAssign(&T, 3, "abc");
    // 将新串T插入S,从下标2开始
    StrInsert(&S, 3, T);
    // 打印串S
    StrPrint(&S); // abcabc

    // 复制串
    printf("*******************\n");
    printf("S串的复制 => P:");
    DString P;
    StrCopy(&P, S);
    StrPrint(&P); // abcabc

    // 串的比较
    printf("*******************\n");
    printf("T、P串的比较:");
    int res = StrCompare(T, P);
    printf("%d\n", res);  // -1

    // 串的长度
    printf("*******************\n");
    printf("S串的长度是:%d\n", StrLength(S)); // 6

    // 合并串
    printf("*******************\n");
    printf("S、P串的合并:");
    DString *newStr;
    StrConcact(&newStr, S, P);
    StrPrint(&newStr); // abcabc abcabc

    // 串的截取
    printf("*******************\n");
    printf("S串的截取:");
    DString newStr1;
    StrSub(&newStr1, S, 1, 5); // abcabc
    StrPrint(&newStr1);

    // 串的匹配
    printf("*******************\n");
    printf("串的匹配:\n");
    char a1[] = "acabaabcaabaabcac";
    char a2[] = "abaabcac";
    DString a;
    StrAssign(&a, 17, a1);
    DString b;
    StrAssign(&b, 8, a2);
    // 打印串S
    StrPrint(&a);
    StrPrint(&b);
    int result1 = StrIndex(a, b, 1);
    int result2 = Index(a, b, 1);

    printf("BF算法匹配字串的位置:%d\n", result1);
    printf("BF算法匹配字串的位置:%d\n", result2);
}

int main() {

    TestDString();

    int next[100];
    char s1[] = "acabaabcaabaabcac";
    char s2[] = "abaabcac";
    int i = KMP(s1, s2);
    printf("%d\n", i);

    system("pause");
    return 0;

}

在这里插入图片描述

返回顶部


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

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

相关文章

【学习笔记77】ajax的函数封装

一、封装分析 &#xff08;一 &#xff09;封装方案 1、回调函数方式 将来使用的时候, 可能会遇到回调地狱 2、promise方式 效果不会有变化, 而且还能和 async/await 一起使用 &#xff08;二&#xff09;函数的参数 请求方式: method > 默认值 get请求地址: url > 必填…

Android开发——Jetpack Compose的使用

Android开发——Jetpack Compose的使用什么是Jetpack ComposeJetpack Compose带来的变化Jetpack Compose的两种运用方法将Jetpack Compose 添加到现有项目1.gradle 配置2.使用Kotlin-Gradle 插件3. 添加依赖项创建一个新的支持Jetpack Compose的项目1.创建一些简单应用定义可组…

12.3做题

一.车队问题 1.思路: 先把所在位置进行排序,升序排序, 计算出每辆车在不受其余车的影响时&#xff0c;行驶到终点需要的时间 从后往前看 对于相邻的两辆车 S 和 F&#xff0c;F 的起始位置大于 S&#xff0c;如果 S 行驶到终点需要的时间小于等于 F&#xff0c;那么 S 一定…

MySQL集群搭建-MMM高可用架构

MySQL集群搭建-MMM高可用架构 原文地址 https://segmentfault.com/a/1190000017286307&#xfeff; 1 MMM 介绍 1.1 简介 MMM 是一套支持双主故障切换以及双主日常管理的第三方软件。MMM 由 Perl 开发&#xff0c;用来管理和监控双主复制&#xff0c;虽然是双主架构&#xff…

volatile

是java虚拟机提供的轻量级的同步机制&#xff08;乞丐版的synchronized) 具备三个性质&#xff1a;保证可见性&#xff0c;不保证原子性&#xff0c;禁止指令重排 前置知识&#xff1a; …

JS读取本地CSV文件数据

JS读取本地CSV文件数据 文件中的部分数据如图 需求是需要提取出文件的数据 使用到的模块是 Papa Parse 1. 依赖安装 yarn add papaparse papaparse的基本使用可以参考官方demo 2. 解析本地文件 首先需要注意, papaparse解析本地文件, 需要的文件格式是从DOM中获得的File…

GO高级特性 之 并发模型

本文介绍一些并发的基础知识、常见的并发模型一级Go语言的MPG并发模型及其运行原理 并发与并行的区别 -并发并行概念并发指同一时间段&#xff0c;多条命令在CPU上同时执行。并行指同一时刻&#xff0c;多条命令在CPU上执行运行原理并发程序不要求计算机有多核计算能力&#…

毕设选题推荐基于python的django框架医疗急诊预约系统

&#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设老哥&#x1f525; &#x1f496; 精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; Java实战项目专栏 Python实…

项目成本管理质量管理

项目成本管理-控制成本目录概述需求&#xff1a;设计思路实现思路分析1.EVM2.偏差指标3.Question:4.绩效指标5.典型偏差TCPIS曲线图绩效审查参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip …

[近两万字] MySQL大全

目录 单元1 创建数据库 1.1 创建数据库 1.查看数据库 2.选择数据库 3.删除数据库 1.2 创建数据表 1.查看表结构 2.查看所有数据表 3.复制表结构 4.删除表 5.修改表数据 5.1 修改表名 5.2 添加字段 5.3删除字段 5.4修改字段的数据类型 5.5修改字段的名称 5.6修改字段…

[网络工程师]-应用层协议-SNMP

简单网络管理协议&#xff08;Simple Network Management Protocol&#xff0c;SNMP&#xff09;是在应用层上进行网络设备间通信的管理协议&#xff0c;可以用于网络状态监视、网络参数设定、网络流量统计与分析、发现网络故障等。SNMP基于UDP协议&#xff0c;由SNMP协议、管理…

【交通建模】基于模型的自主交通仿真框架附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

【深入浅出Java并发编程指南】「难点 - 核心 - 遗漏」让我们一起探索一下CyclicBarrier的技术原理和源码分析

CyclicBarrier和CountDownLatch CyclicBarrier和CountDownLatch 都位于java.util.concurrent这个包下&#xff0c;其工作原理的核心要点&#xff1a; CyclicBarrier工作原理分析 那么接下来给大家分享分析一下JDK1.8的CyclicBarrier的工作原理。 简单认识CyclicBarrier 何为…

Nginx动静分离、缓存配置、性能调优、集群配置

一. Nginx动静分离 1. 准备 1个web程序&#xff1a;部署在7061端口&#xff0c;启动 【dotnet NginxWeb.dll --urls"http://*:7061" --ip"127.0.0.1" --port7061】 Nginx程序&#xff1a;监听7000端口 2. 目的 比如单独启动部署在7061端口下的web程序&am…

c++ 静态库,动态库的制作和使用

文章目录1.什么是库&#xff1f;2.静态库的制作1.静态库的命名规则2.静态库的制作与使用1.静态库的制作2.静态库的使用3.动态库的制作1.动态库的命名规则2.动态库的制作与使用1.动态库的制作2.动态库的使用3.动态库加载失败的原因4.静态库和动态库的对比1.程序编译成可执行文件…

[附源码]Python计算机毕业设计Django基于Java的失物招领平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

时序数据库选型

目录一、当前主流的时序数据库二、各库特性1、Influxdb2、Timescale3、Druid4、Kdb5、Graphite6、RRDtool7、OpenTSDB8、TDengine9、DolphinDB三、优缺点对比因为个人用的go&#xff0c;所以调研及对比主要针对适配了go语言的数据库。 一、当前主流的时序数据库 排名参考于h…

基于PHP+MySQL动漫专题网站系统的设计与实现

随着时代的发展&#xff0c;人们兴趣爱好也越来越广泛&#xff0c;动漫是当前年轻人比较钟爱的一个兴趣爱好之一&#xff0c;每年都会有大量的动漫爱好者定期的举办一些交流活动等&#xff0c;但是线下的这种交流方式明显不能满足当前动漫爱好者的需求&#xff0c;为此我开发了…

Zen of Python(python之禅)

在python中导入import this就会显式&#xff1a; 以下是中文英文翻译&#xff1a; Beautiful is better than ugly. 优美比丑陋好 Explicit is better than implicit. 清晰比晦涩好 Simple is better than complex. 简单比复杂好 Complex is better than complicated. 复杂比错…

【全干工程师必学】一文搞懂Vue2.0

一文搞懂Vue2.0一、前端工程化二、WebPack是什么基本使用实现奇偶行变色1.初始化包管理工具2.安装jquery3.在项目中安装webpack4.在项目中配置webpack5.运行代码mode 的可选值developmentproductionwebpack.config.js 文件的作用webpack 中的默认约定自定义打包的入口与出口web…