Chiriri's blog Chiriri's blog
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)

Iekr

苦逼后端开发
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)
  • 数据结构

  • 数据结构与算法

    • 绪论
    • 线性表
    • 栈和队列
    • 串
      • 定义基本操作
        • 串的定义
        • 串的基本操作
        • 字符集编码
      • 串的存储结构
        • 串的顺序存储
        • 串的链式存储
        • 基本操作的实现
        • 总结
      • 字符串模式匹配
        • 朴素模式匹配算法
        • 总结
        • KMP算法
        • 朴素模式匹配算法优化思路
        • KMP算法
        • next数组
        • 求模式串的next数组(手算练习)
        • 使用next数组进⾏模式匹配
    • 树与二叉树
    • 图
    • 查找
    • 排序
  • 计算机组成原理

  • 操作系统

  • 408
  • 数据结构与算法
Iekr
2022-11-26
目录

串

# 串

# 定义基本操作

# 串的定义

串,即字符串(String)是由零个或多个字符组成的有限序列。一般记为S=′a1a2⋯⋯an′(n≥0)

其中,S 是串名,单引号括起来的字符序列是串的值;ai 可以是字母、数字或其他字符;串中字符的个数 n 称为串的长度。n=0 时的串称为空串(用ϕ 表示)。

有的地方用双引号(如 Java、C), 有的地方用单引号(如 Python)

S="HelloWorld!";
1
T='iPhone 11 Pro Max?'
1
  • 子串: 串中任意个连续的字符组成的子序列。 Eg:’iPhone’,’Pro M’ 是串 T 的子串
  • 主串: 包含子串的串。 Eg:T 是子串’iPhone’的主串
  • 字符在主串中的位置: 字符在串中的序号。 Eg:’1’在 T 中的位置是 8 (第一次出现)
  • 子串在主串中的位置: 子串的第一个字符在主串中的位置 。 Eg:’11 Pro’在 T 中的位置为 8;注意:位序从 1 开始而不是从 0 开始
M=''; //M是空串
N='   '; //N是由三个空格字符组成的空格串,每个空格字符占1B
1
2

串是一种特殊的线性表,数据元素之间呈线性关系

image-20221126095738427

串的数据对象限定为字符集(如中文字符、英文字符、数字字符、标点字符等)

串的基本操作,如增删改查等通常以子串为操作对象

# 串的基本操作

  • StrAssign(&T,chars) :赋值操作。把串 T 赋值为 chars。
  • StrCopy(&T,S) :复制操作。由串 S 复制得到串 T。
  • StrEmpty(S) :判空操作。若 S 为空串,则返回 TRUE,否则返回 FALSE。
  • StrLength(S) :求串长。返回串 S 的元素个数。
  • ClearString(&S) :清空操作。将 S 清为空串。
  • DestroyString(&S) :销毁串。将串 S 销毁(回收存储空间)。
  • Concat(&T,S1,S2) :串联接。用 T 返回由 S1 和 S2 联接而成的新串
  • SubString(&Sub,S,pos,len) :求子串。用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串。
  • Index(S,T) :定位操作。若主串 S 中存在与串 T 值相同的子串,则返回它在主串 S 中第一次出现的位置;否则函数值为 0。
  • StrCompare(S,T) :比较操作。若 S>T,则返回值 > 0;若 S=T,则返回值 = 0;若 S<T,则返回值 < 0。
    • 从第一个字符开始往后依次对比,先出现更大字符的串就更大
    • 长串的前缀与短串相同时,长串更大
    • 只有两个串完全相同时,才相等

# 字符集编码

任何数据存到计算机中一定是二进制数。需要确定一个字符和二进制数的对应规则这就是 “编码”

“字符集”:英文字符 ——ASCII 字符集,中英文 ——Unicode 字符集等多种编码

注:采用不同的编码方式,每个字符所占空间不同,考研中只需默认每个字符占 1B 即可

# 串的存储结构

# 串的顺序存储

image-20221126101257070

#define MAXLEN 255 //预定义最大串长为255
typedef struct {
	char ch[MAXLEN]; //每个分量存储一个字符
	int length; //串的实际长度
} SString;


typedef struct {
	char *ch; //按串长分配存储区,ch指向串的基地址
	int length; //串的长度
} HString;

void testString() {
	HString S;
	S.ch = (char *)malloc(MAXLEN * sizeof(char)); // 用完需要手动free
	S.length = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

image-20221126101514424

下面代码默认会从下标 1 开始访问,ch [0] 废弃不用。

# 串的链式存储

typedef struct StringNode {
	char ch; //每个结点存储1个字符
	struct StringNode * next;
} StringNode, *String;
1
2
3
4

image-20221126101801812

#define N 4
typedef struct StringNode {
	char ch[N]; //每个结点存储多个字符
	struct StringNode * next;
} StringNode, *String;
1
2
3
4
5

image-20221126101844565

# 基本操作的实现

image-20221126101931250

  • SubString(&Sub,S,pos,len) :求子串。用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串。
#define MAXLEN 255 //预定义最大串长为255
typedef struct {
	char ch[MAXLEN]; //每个分量存储一个字符
	int length; //串的实际长度
} SString;

//求子串
bool SubString(SString &Sub, SString S, int pos, int len) {
	//子串范围越界
	if (pos + len - 1 > S.length) {
		return false;
	}
	for (int i = pos; i < pos + len; i++)
	{
		Sub.ch[i - pos + 1] = S.ch[i];
	}
	Sub.length = len;
	return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  • StrCompare(S,T) :比较操作。若 S>T,则返回值 > 0;若 S=T,则返回值 = 0;若 S<T,则返回值 < 0。
//比较操作
int StrCompare(SString S, SString T) {
	for (int i = 1; i <= S.length && i <= T.length; i++)
	{
		if (S.ch[i] != T.ch[i]) {
			return S.ch[i] - T.ch[i];
		}
	}
	// 扫描过的所有字符都相同,则长度长的串更大
	return S.length - T.length;
}
1
2
3
4
5
6
7
8
9
10
11
  • Index(S,T) :定位操作。若主串 S 中存在与串 T 值相同的子串,则返回它在主串 S 中第一次出现的位置;否则函数值为 0。
//定位操作
int Index(SString S, SString T) {
	int i = 1, n = SS.length, m = SS.length;
	SString sub; //存储子串
	while (i < n - m + 1) {
		SubString(sub, S, i, m);
		if (StrCompare(sub, T) != 0) {
			++i;
		} else {
			return i; //返回子串在主串中的位置
		}
	}
	return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 总结

image-20221127063325855

# 字符串模式匹配

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

image-20221127063450381

  • 子串 —— 主串的⼀部分,⼀定存在
  • 模式串 —— 不⼀定能在主串中找到

# 朴素模式匹配算法

主串长度为 n,模式串长度为 m

朴素模式匹配算法:将主串中所有长度为 m 的子串依次与模式串对比,直到找到⼀个完全匹配的子串,或所有的子串都不匹配为止。

image-20221127063936143

若当前⼦串匹配失败,则主串指针 i 指向下⼀个⼦串的第⼀个位置,模式串指针 j 回到模式串的第⼀个位置

//朴素模式匹配算法
int Index(SString S, SString T) {
	int i = 1, i = 1;
	while (i < S.length && j < T.length) {
		if (S.ch[i] == T.ch[j]) {
			++i; ++j; //继续比较后继字符
		} else {
			i = i - j + 2;
			j = 1; //指针后退重新开始匹配
		}
	}
	if (j > T.length) {
		return i - T.length;
	} else {
		return 0;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

最坏时间复杂度 = O(nm) 最好时间复杂度 = O(n)

# 总结

image-20221127064535194

# KMP 算法

由 D.E.Knuth,J.H.Morris 和 V.R.Pratt 提出,因此称为 KMP 算法

# 朴素模式匹配算法优化思路

image-20221127065600383

image-20221127065715477

怎么用代码实现这个处理逻辑?

next 数组:

image-20221127070058090

next 数组只和短短的模式串有关,和长的主串⽆关

# KMP 算法

image-20221127070152244

// 求next数组
void get_next(SString S,,int next[]){
    int i=1;j=0;
    next[1]=0;
    while(i<T.length){
        if(j==0||T.ch[i]==T.ch[j]){
            ++i; ++j;
            next[i]=j;
        } else {
            j=next[j];
        }
    }
}


// kmp算法
int Index_KMP(SString S,SString T,int next[]){
    int i=1 , j=1;
    while(i<=S.length&&j<T.length){
        if(j==0||S.ch[i]==T.ch[j]){
            ++i;
            ++j;
        } else{
            j=next[j];
        }
        if(j>T.length){
            return i-T.length;
        } else{
          return 0;  
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

朴素模式匹配 v.s. KMP 算法

image-20221127070322423

KMP 算法精髓:利用好已经匹配过的模式串的信息

# next 数组

# 求模式串的 next 数组(手算练习)

image-20221127073803147

image-20221127073847403

image-20221127073956496

image-20221127074006687

image-20221127074014496

image-20221127074049647

image-20221127074113987

image-20221127074122318

image-20221127074133398

image-20221127074203722

image-20221127074212156

image-20221127074228790

image-20221127074243488

image-20221127074302644

image-20221127074316904

image-20221127074324839

image-20221127074332515

image-20221127074341037

image-20221127074745672

image-20221127074759062

  1. next [1] 固定为 0
  2. next [2] 固定 为 1
  3. 其他的 next 元素,在不匹配的位置前边,划⼀根美丽的分界线模式串⼀步⼀步往后退,直到分界线之前 “能对上”,或模式串完全跨过分界线为止。
# 使用 next 数组进⾏模式匹配

image-20221127074820363

image-20221127074835829

image-20221127074859701

image-20221127074906325

image-20221127074927167

image-20221127074946141

image-20221127074955512

image-20221127075008012

编辑 (opens new window)
上次更新: 2023/12/06, 01:31:48
栈和队列
树与二叉树

← 栈和队列 树与二叉树→

最近更新
01
k8s
06-06
02
进程与线程
03-04
03
计算机操作系统概述
02-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Iekr | Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式