数据结构中的串(String):概念、操作和实际应用

server/2025/1/16 0:18:21/

目录

一.引言

二.串的定义 

三. 串的抽象数据类型(ADT)

四. 串的存储结构

顺序存储结构

链式存储结构

五. 串模式的存储算法

KMP算法(Knuth-Morris-Pratt算法

2.Brute-Force(暴力匹配)算法

3.Boyer-Moore算法

4.Rabin-Karp算法

六. 实际案例以及应用

七.总结


一.引言

        在计算机科学中,数据结构是一种用于组织和存储数据的方式。其中,串(String)是一种非常常见的数据结构,用于表示一组字符序列。串通常用于处理文本数据,如文本编辑器、搜索引擎、数据库系统等。在本文中,我们将深入探讨串的概念、操作以及实际应用。

二.串的定义 

        在计算机科学中,串 是由零个或多个字符组成的有限序列,是数据结构中常见的一种基本类型。它可以为空串,也可以是由字符组成的非空串。串通常用于表示文本数据,如文件内容、用户输入等,因此在算法和程序设计中占据着重要的地位。

        一个串可以是由字母、数字、符号等任意字符组成,字符的种类和个数没有固定限制。例如,"Hello, World!"、"123456"、"!@#$%" 都是串的例子。

        串的重要性在于它是对文本数据进行处理和操作的基础,涉及字符串匹配、文本编辑、编译器设计等多个领域。因此,深入理解串的特性和操作对于程序设计人员至关重要。

三. 串的抽象数据类型(ADT)

        在计算机科学中,为了方便对串进行操作和管理,可以定义串的抽象数据类型(ADT),以提供一组操作和属性来描述串的特性。下面是串的抽象数据类型的定义

// 串的抽象数据类型定义
typedef struct {char *str;  // 存储串的字符数组int length; // 串的长度
} String;

        在这个抽象数据类型中,str是一个指向字符数组的指针,用于存储串的内容,length表示串的长度。通过这个抽象数据类型,我们可以方便地对串进行创建、销毁、获取长度等操作,从而实现对串的高效管理和处理。

四. 串的存储结构

        串的存储结构是指如何在计算机内存中存储串的数据以及相关信息。常见的存储结构有两种:顺序存储结构和链式存储结构。下面分别介绍这两种存储方式,并给出相应的代码实现。

顺序存储结构

        顺序存储结构是将串中的字符顺序地存放在一段连续的存储空间中,通常是字符数组。下面是顺序存储结构的定义:

#define MAX_SIZE 100 // 假设串的最大长度为100typedef struct {char str[MAX_SIZE]; // 用字符数组存储串int length;         // 串的长度
} SeqString;

        在顺序存储结构中,str是一个字符数组,用于存储串的内容,length表示串的长度。通过这种方式,可以直接访问串中的任意字符,并且可以快速获取串的长度。

链式存储结构

        链式存储结构是通过链表来存储串的字符,每个节点存储一个字符,并且通过指针将各个节点连接起来。下面是链式存储结构的定义:

typedef struct LNode {char data;struct LNode *next;
} LNode;typedef struct {LNode *head; // 头指针int length;  // 串的长度
} LinkString;

        在链式存储结构中,每个节点中的data存储一个字符,next指针指向下一个节点。头指针head指向链表的第一个节点,通过遍历链表可以获取串的全部字符。链式存储结构适合于不确定串长度的情况,但需要额外的指针开销。

五. 串模式的存储算法

        串模式的存储算法主要用于实现串的模式匹配,下面是串模式常用的几种存储算法

1.KMP算法(Knuth-Morris-Pratt算法

  • KMP算法是一种高效的字符串匹配算法,通过预处理模式串,利用模式串中的信息在匹配失败时尽可能地减少比较次数,从而提高匹配效率。
  • KMP算法的核心思想是利用模式串内部的信息,在匹配失败时根据已匹配的部分,选择合适的位置进行模式串的移动。
  • 算法的关键在于部分匹配表(Next数组),它记录了模式串每个位置之前的子串中有多大长度的相同前缀和后缀。
  • 匹配过程中,当文本串的某个字符与模式串的对应字符不匹配时,利用Next数组确定模式串的移动位置,从而减少不必要的比较。

特点与应用:

  • KMP算法的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。在实际应用中,KMP算法通常比暴力匹配算法具有更高的效率。
  • 由于其高效的匹配速度,KMP算法被广泛应用于字符串匹配、文本搜索、编译器设计等领域。
  • 在处理大规模文本数据时,KMP算法能够提供更快速和可靠的匹配方案,因此被视为处理字符串匹配问题的重要工具之一。

下面是KMP算法的核心部分:

void getNext(char *pattern, int next[]) {int len = strlen(pattern);next[0] = -1;int k = -1, j = 0;while (j < len - 1) {if (k == -1 || pattern[j] == pattern[k]) {++k;++j;next[j] = k;} else {k = next[k];}}
}int KMP(char *text, char *pattern) {int len1 = strlen(text);int len2 = strlen(pattern);int i = 0, j = 0;int *next = (int *)malloc(sizeof(int) * len2);getNext(pattern, next);while (i < len1 && j < len2) {if (j == -1 || text[i] == pattern[j]) {++i;++j;} else {j = next[j];}}free(next);if (j == len2) {return i - j; // 匹配成功,返回模式串在文本串中的起始位置} else {return -1; // 匹配失败}
}

2.Brute-Force(暴力匹配)算法

  • 暴力匹配算法是一种简单直接的字符串匹配算法,也称为朴素字符串匹配算法。它的基本思想是从文本串的第一个字符开始,与模式串进行逐个字符比较,若匹配失败,则将模式串右移一位,继续比较,直到找到匹配或者文本串被遍历完为止。
  • 算法的时间复杂度为O(n*m),其中n为文本串的长度,m为模式串的长度。

以下是暴力匹配算法的核心部分的伪代码表示:

BruteForceSearch(text, pattern):n = 文本串的长度m = 模式串的长度for i from 0 to n - m do:j = 0while j < m 且 text[i + j] == pattern[j] do:j = j + 1if j == m:  # 找到了匹配返回 i  # 返回匹配位置的起始下标返回 -1  # 没有找到匹配

3.Boyer-Moore算法

  • Boyer-Moore算法是一种高效的字符串匹配算法,其核心思想是利用模式串中的字符出现位置的信息,在匹配失败时尽可能地跳过多的字符,从而减少比较次数。
  • 算法包含两个启发式规则:坏字符规则和好后缀规则,通过这两个规则可以确定每次匹配失败时模式串的移动位置。
  • Boyer-Moore算法的时间复杂度在最坏情况下为O(n*m),但通常情况下具有线性时间复杂度。

以下是Boyer-Moore算法的核心部分的伪代码表示:

BoyerMooreSearch(text, pattern):n = 文本串的长度m = 模式串的长度lastOccurrence = 记录模式串中每个字符最后出现位置的数组suffix = 计算模式串的后缀数组prefix = 计算模式串的前缀数组初始化lastOccurrence数组,记录模式串中每个字符最后出现的位置初始化suffix数组,计算模式串的后缀数组初始化prefix数组,计算模式串的前缀数组i = m - 1  # 文本串中与模式串最后一个字符对齐的位置j = m - 1  # 模式串中最后一个字符的下标while i < n do:if text[i] == pattern[j]:  # 逐个字符比较if j == 0:  # 如果模式串已经比较完毕,说明找到了匹配返回 i  # 返回匹配位置的起始下标else:i = i - 1  # 继续向前比较j = j - 1else:badCharacterShift = j - lastOccurrence[text[i]]  # 坏字符规则计算位移goodSuffixShift = calculateGoodSuffixShift(j, suffix, prefix)  # 好后缀规则计算位移i = i + max(badCharacterShift, goodSuffixShift)  # 取较大的位移j = m - 1  # 重新比较模式串的最后一个字符返回 -1  # 没有找到匹配calculateGoodSuffixShift(j, suffix, prefix):k = m - 1 - j  # 好后缀的长度if suffix[k] != -1:  # 如果找到匹配的好后缀return j - suffix[k] + 1else:  # 如果找不到匹配的好后缀for r from j + 2 to m - 1:if prefix[m - r]:return rreturn m  # # 如果没有好后缀匹配,则移动整个模式串长度

4.Rabin-Karp算法

  • Rabin-Karp算法是一种基于哈希的字符串匹配算法,它通过对文本串和模式串进行哈希计算,然后比较它们的哈希值来判断是否匹配。
  • 算法的优势在于可以在O(n+m)的时间复杂度内完成匹配,且在某些情况下比其他算法更加高效。
  • 然而,Rabin-Karp算法的实现涉及到哈希函数的设计和哈希冲突的处理,因此需要谨慎选择哈希函数以及解决哈希冲突的方法。

以下是Rabin-Karp算法的核心部分的伪代码表示:

RabinKarpSearch(text, pattern):n = 文本串的长度m = 模式串的长度p = 一个较大的素数,用于哈希计算textHash = 计算文本串text[0:m]的哈希值patternHash = 计算模式串pattern的哈希值for i from 0 to n - m do:if textHash == patternHash 且 text[i:i+m] == pattern:  # 判断哈希值相等且子串相等返回 i  # 返回匹配位置的起始下标if i < n - m:  # 更新下一次文本串的哈希值textHash = 重新计算text[i+1:i+m+1]的哈希值返回 -1  # 没有找到匹配

六. 实际案例以及应用

        串在计算机科学中有着广泛的应用,比如字符串匹配、文本编辑器、编译器等领域。

下面我们以文本编辑器为例进行说明。

首先,我们需要定义一些常量和全局变量:

#define MAX_LENGTH 1000char text[MAX_LENGTH];
int length = 0;

MAX_LENGTH 是文本的最大长度,text 是用于存储文本的字符数组,length 是当前文本的长度。

然后,我们定义一些函数来实现文本编辑器的功能:

void insert(int pos, char *str) {int len = strlen(str);if (length + len > MAX_LENGTH) {printf("Error: Text is too long\n");return;}for (int i = length; i >= pos; i--) {text[i + len] = text[i];}for (int i = 0; i < len; i++) {text[pos + i] = str[i];}length += len;
}void delete(int pos, int len) {if (pos + len > length) {printf("Error: Invalid position or length\n");return;}for (int i = pos; i < length - len; i++) {text[i] = text[i + len];}length -= len;
}void replace(int pos, int len, char *str) {delete(pos, len);insert(pos, str);
}int find(char *str) {int i, j, k;for (i = 0; i <= length - strlen(str); i++) {for (j = i, k = 0; text[j] == str[k]; j++, k++) {if (str[k + 1] == '\0') {return i;}}}return -1;
}

        insert 函数用于在指定位置插入一个字符串,delete 函数用于删除指定位置的字符串,replace 函数用于替换指定位置的字符串,find 函数用于查找指定的字符串。

        最后,我们编写主函数来测试这些函数:

int main() {insert(0, "Hello, ");insert(7, "World!");printf("Text: %s\n", text);replace(7, 5, "Dear");printf("Text: %s\n", text);int pos = find("Dear");if (pos != -1) {printf("Substring 'Dear' found at position %d\n", pos);} else {printf("Substring 'Dear' not found\n");}return 0;
}
运行这段代码,你将看到以下输出:Text: Hello, World!
Text: Hello, Dear!
Substring 'Dear' found at position 7

七.总结

        串是一种非常常见的数据结构,用于表示一组字符序列。它支持多种操作,如插入、删除、替换、查找、比较和连接。串在现实生活中有许多应用,如文本编辑器、搜索引擎、数据库系统和编译器等。


http://www.ppmy.cn/server/19071.html

相关文章

Elasticsearch知识点表格总结

最近其实有点忙&#xff0c;在主攻Django项目和go语言项目&#xff0c;所以只能利用下班晚上和周末总结一些知识点&#xff0c;因为最近的公司的项目没有用上Elasticsearch&#xff0c;所以总结一下&#xff0c;方便以后复习&#xff0c;以及以后面试会用上&#xff0c;不然过段…

python——处理excel的常用库

Python 处理 Excel 文件主要依赖于几个流行的第三方库&#xff0c;这些库提供了丰富的功能来读取、写入以及操作 Excel 文件。以下是几种常见的处理方式&#xff1a; pandas: 安装: pip install pandas openpyxl&#xff08;或pip install pandas xlrd xlwt&#xff0c;取决于E…

TCP相关问题总结

文章目录 TCP连接建立过程1. TCP三次握手2. TCP四次挥手3. TCP为什么是三次握手4. TCP为什么是四次挥手 TCP流量控制TCP拥塞控制1. 为什么需要拥塞控制2. 控制手段 TCP连接建立过程中出现丢包 TCP连接建立过程 1. TCP三次握手 首先client端发出连接请求&#xff0c;并且请求同…

Python编程----递归求解兔子的数量

描述 兔子的数量以这样的方式增长&#xff1a;每个月的兔子数量等于它前一个月的兔子数量加它前两个月的兔子数量&#xff0c;即f(n)f(n-1)f(n-2)。假设第1个月的兔子有2只&#xff0c;第2个月的兔子有3只&#xff0c;你能使用递归的方法求得第n个月的兔子有多少只吗&#xff…

Web3技术解析:区块链在去中心化应用中的角色

引言 在过去几年中&#xff0c;Web3技术已经成为了互联网领域的一个热门话题。作为区块链技术的延伸&#xff0c;Web3不仅仅是数字货币的代名词&#xff0c;更是一个能够为各种应用提供去中心化解决方案的强大工具。本文将深入探讨区块链在Web3去中心化应用中的关键角色&#…

卸载微软的浏览器: Edge

前言&#xff1a; Edge 崩溃了&#xff0c;无法访问网路&#xff1a; 错误代码: STATUS_STACK_BUFFER_OVERRUN 然后&#xff0c;windows不提供卸载&#xff0c;这下好了&#xff0c;它不能用&#xff0c;你也不能卸载&#xff0c;重新安装也无法解决&#xff0c;咋办&#xff…

电脑安装双系统

在一台电脑上安装Linux和Windows的双系统可以让你在同一硬件上运行两种操作系统。以下是安装Linux和Windows双系统的一般步骤&#xff1a; 步骤1: 备份数据 在进行任何操作系统安装或重大更改之前&#xff0c;首先备份你的重要数据&#xff0c;以防万一出现问题。 步骤2: 准…

MySQL简解

文章目录 1. MySQL框架2. 执行流程2.1. 连接池&#xff1a;2.2. SQL 前端(SEVER)2.2.0. 查询缓存2.2.1. SQL 接口2.2.2. SQL 解析器2.2.3. SQL 执行器2.2.4. INNODB 中读写操作 2.3. 数据的保存形式 3.其他重要概念3.1. 索引3.1.1. 简单概念3.1.2. 索引优化&#xff1a;1. Usin…