第5章 串
- 串(string) 是由零个或多个字符组成的有限序列, 又名叫字符串。
5.1 开场白
5.2 串的定义
- 定义:串(string) 是由零个或多个字符组成的有限序列, 又名叫字符串(序列说明串的相邻字符之间具有前驱和后继的关系)
- 特点:一般记为
s="a1a2......an"(n≥0)
, s是串的名称,用双引号括起来的字符序列是串的值,ai(1≤i≤n) 可以是字母、 数字或其他字符,n称为串的长度 - 空串:零个字符的串称为空串,可以用两双引号表示, 也可以用希腊字母Φ来表示。
- 空格串:不同于空串,串的值可以是1个或多个空格
- 子串与主串: 串中任意个数的连续字符组成的子序列称为该串的子串, 相应地, 包含
子串的串称为主串。子串在主串中的位置就是子串的第一个字符在主串中的序号。
5.3 串的比较
-
串的比较是通过组成串的字符之间的编码来进行的, 而字符的编码指的是字符在对应字符集中的序号
-
ASCII编码:由8位二进制数表示一个字符, 总共可以表示256个字符
-
Unicode编码:16位的二进制数表示一个字符, 这样总共就可以表示216个字符, 约是6.5万多个字符, 足够表示世界上所有语言的所有字符了。 而且为了和ASCII码兼容, Unicode的前256个字符与ASCII码完全相同。
-
比较方法:当满足以下条件之一时, s<t (其中n为s的长度,m为t的长度)
- n<m, 且ai=bi
- 存在某个k≤min(m, n) , 使得ai=bi(i=1, 2, ……, k-1) , ak<bk。
5.4 串的抽象数据类型
- 与线性表的比较:线性表更关注的是单个元素的操作, 比如查找一个元素, 插入或删除一个元素;但串中更多的是查找子串位置、 得到指定位置子串、 替换子串等操作。
- 结构
不同的编程语言可能还会有其他复杂的操作,这些操作是上面这些基本操作的扩展函数
5.5 串的存储结构
5.5.1 串的顺序存储结构
- 定义:用一组地址连续的存储单元来存储串中的字符序列的。 按照预定义的大小, 为每个定义的串变量分配一个固定长度的存储区。 一般是用定长数组来定义。
- 特点:串的顺序存储方式其实是有问题的,因为串的一些操作有可能使得串序列的长度超过了数组的长度,所以对于串的顺序存储, 有一些变化, 串值的存储空间可在程序执行过程中动态分配而得。 比如在计算机中存在一个自由存储区, 叫做**“堆”**。 这个堆可由C语言的动态分配函数malloc()和free()来管理。
5.5.2 串的链式存储结构
- 定义:如果一个结点对应一个字符, 就会存在很大的空间浪费。 因此, 一个结点可以存放一个字符, 也可以考虑存放多个字符, 最后一个结点若是未被占满时, 可以用“#”或其他非串值字符补全。
- 两种存储结构的对比:串的链式存储结构除了在连接串与串操作时有一定方便之外, 总的来说不如顺序存储灵活, 性能也不如顺序存储结构好。
5.6 朴素的模式匹配算法
- 子串的定位操作通常称做串的模式匹配。
- 该算法先遍历主串,再遍历字串,效率很低,最坏情况的时间复杂度为O((n-m+1)*m) ,其中n为主串长度, m为要匹配的子串长度
5.7 KMP模式匹配算法
- 全称:努特—莫里斯—普拉特算法
5.7.1 KMP模式匹配算法原理
- 如果我们知道子串T中首字符“a”与T中后面的字符均不相等,而T串的第二位的“b”与主串S中第二位的“b”已经判断是相等的, 那么也就意味着, T串中首字符“a”与S串中的第二位“b”是不需要判断也知道它们是不可能相等了, 这样这一步判断是可以省略的。
- 主串(文本串)下标i通过不断地回溯来完成;子串(模式串)下标j值取决于当前字符之前的串的前后缀的相似度,与主串无关
- 把T串各个位置的j值的变化定义为一个数组next(即通过当前j值获得下一个j值)
5.7.2 next数组值推导
可以通过下面例子来理解上面公式
- 例1
- 例2
- 例3
- 例4
5.7.3 KMP模式匹配算法实现
这个地方推荐大家去Carl代码随想录中看KMP算法的讲解,非常详细易懂。可以点击链接。