写在前面:本文仅供个人学习使用。《大话数据结构》通俗易懂,适合整体做笔记输出,构建体系。并且文中很多图片来源于该书。

文章目录

  • 5.2 串的定义
  • 5.3串的比较
  • 5.4串的抽象数据类型
  • 5.5 串的存储结构
    • 5.5.1 串的顺序存储结构
    • 5.5.2 串的链式存储结构
  • 5.6朴素的模式匹配算法
  • 5.7 KMP模式匹配算法
    • 5.7.1 KMP模式匹配算法原理
    • 5.7.2 next数组值推导
    • 5.7.3 KMP模式匹配算法实现
    • 5.7.4 KMP模式匹配算法改进
    • 5.7.5 nextval数组值推导

串:串(string)是由零个或多个字符组成的有限序列,又名叫字符串。

5.2 串的定义

早先的计算机在被发明时,主要作用是做一些科学和工程的计算工作,也就是现在我们理解的计算器,只不过它比小小的计算器功能更强大、速度更快一些。后来发现,在计算机上作非数值处理的工作越来越多,使得我们不得不需要引入对字符的处理。 于是就有了字符串的概念。

比如我们现在常用的搜索引擎,当我们在文本框中输入“数据”时,它已经把我们想要的“数据结构“列在下面了。显然这里网站作了一个字符串查找匹配的工作,如图所示。


今天我们就来研究”串“这样的数据结构,先来看定义。串(string) 是由零个或多个字符组成的有限序列,又名叫字符串。

一般记为s="a1a2...an(n≥0)"s="a_1a_2...a_n(n≥0)"s="a1​a2​...an​(n≥0)",其中,s是串的名称,用双引号括起来的字符序列是串的值,注意引号不是串的内容。aia_iai​可以是字母、数字或其他字符,i就是该字符在串中的位置。串中的字符数目n称为串的长度,定义中谈到”有限“是指长度n是一个有限的数值。零个字符的串称为空串(null string),它的长度为零,可以直接用两双引号”""“表示,也可以用希腊字母Φ\PhiΦ来表示。所谓的序列,说明串的相邻字符之间具有前驱和后继的关系。

还有一些概念需要解释。

空格串,是只包含空格的串。注意它与空串的区别,空格串是有内容有长度的,而且可以不止一个空格。
子串与主串,串中任意个数的连续字符组成的子序列称为该串的子串,相应地,包含子串的串称为主串。

子串在主串中的位置就是子串的第一个字符在主串中的序号。

5.3串的比较

两个数字,很容易比较大小。2比1大,这完全正确,可是两个字符串如何比较? 比如”silly“ ”stupid“ 这样的同样表达”愚蠢的“的单词字符串,它们在计算机中的大小其实取决于它们挨个字母的前后顺序。 它们的第一个字母都是”s“,我们认为不存在大小差异,而第二个字母,由于”i“字母比”t“字母要靠前,所以”i“<“t”,于是我们说”silly“<“stupid”。

事实上,串的比较是通过组成串的字符之间的编码来进行的,而字符的编码指的是字符在对应字符集中的序号。

计算机中常用的字符使用ASCII编码,更准确一点,由7位二进制数表示一个字符,总共可以表示128个字符。后来发现由于一些特殊字符的出现,128个不够用,于是扩展ASCII码由8位二进制数表示一个字符,总共可以表示256个字符,这已经足够满足以英语位主的语言和特殊符号进行输入、存储、输出等操作的字符需要了。 可是,单我们国家就有除汉族外的满、回、藏、蒙古等多个少数民族文字,换作全世界估计要有成百上千种语言和文字,显然这256个字符是不够的,因此后来就有了Unicode编码,比较常用的是由16位的二进制数表示一个字符,这样总共就可以表示2162^{16}216个字符,约是65万多个字符,足够表示世界上所有语言的所有字符了。当然,为了和ASCII兼容,Unicode的前256个字符与ASCII码完全相同。

所以,如果我们要在C语言中比较两个串是否相等,必须是它们串的长度以及它们各自对应位置的字符都相等时,才算是相等。 即给定两个串:s="a1a2...an"s="a_1a_2...a_n"s="a1​a2​...an​",t="b1b2...bm"t="b_1b_2...b_m"t="b1​b2​...bm​",当且仅当n=m,且a1=b1,a2=b2,...an=bma_1=b_1,a_2=b_2,...a_n=b_ma1​=b1​,a2​=b2​,...an​=bm​时,我们认为s=t。

那么对于两个不相等的串,如何比较它们的大小呢?我们这样定义:
给定两个串:s="a1a2...an"s="a_1a_2...a_n"s="a1​a2​...an​",t="b1b2...bm"t="b_1b_2...b_m"t="b1​b2​...bm​",当满足以下条件之一时,有s<t.

  1. n<m,且ai=bi(i=1,2,...n)a_i=b_i(i=1,2,...n)ai​=bi​(i=1,2,...n) 例如 当s=“hap”,t=“happy”,就有s<t.因为t比s多出了两个字母。
  2. 存在某个k≤min(m,n),使得ai=bi(i=1,2,...,k−1),而ak<bk。a_i=b_i(i=1,2,...,k-1),而a_k<b_k。ai​=bi​(i=1,2,...,k−1),而ak​<bk​。例如当s=“happen”,t=“happy”,因为两串的前4个字母均相同,而两串第5(k值)个字母,字母e的ASCII码是101,而字母y的ASCII码是121,显然e<y,所以s<t.

说白了,就是字典序,即字典中排列的顺序。

5.4串的抽象数据类型

串的逻辑结构和线性表很相似,不同之处在于串针对的是字符集,也就是串中的元素都是字符。因此,对于串的基本操作和线性表的操作是有很大差别的。线性表更关注的是单个元素的操作,比如查找一个元素,插入或删除一个元素,但串中更多的是查找子串的位置、得到指定位置的子串、替换子串等操作。

ADT 串(string)
Data串中元素仅由一个个字符组成,相邻元素具有前驱和后继关系。
OperationStrAssign(T,*chars):生成一个其值等于字符串常量chars的串T。StrCopy(T,S):串s存在,有串s复制得到串TClearString(S):串s存在,将串清空StringEmpty(S):若串S为空,返回true,否则返回falseStrLength(S):返回串S的元素个数,即串的长度StrCompare(S,T):若S>T,返回值>0,若S=T,返回0;若S<T,返回值<0Concat(T,S1,S2):用T返回由S1和S2连接而成的新串SubString(Sub,S,pos,len):串S存在,1≤pos≤StrLength(S),其0≤len≤StrLength(S)-pos+1,用Sub返回串S的第pos个字符起长度为len的子串Index(S,T,pos):串S和T存在,T是非空串,1≤pos≤StrLength(S),若主串S中存在和串T值相同的子串,则返回它在主串S中第pos个字符之后第一次出现的位置,否则返回0Replace(S,T,V):串S、T、V存在,T是非空串。用V替换主串S中出现的所有与T相等的不重叠的子串。StrInsert(S,pos,T):串S和T存在,1≤pos≤StrLength(S)+1.在串S的第pos个字符之前插入串TStrDelete(S,pos,len):串S存在,1≤pos≤StrLength(S)-len+1,从串S中删除第pos个字符起长度为len的子串。
endADT

对于不同的高级语言,对于串的基本操作会有不同的定义方法,所以在用某个语言操作字符串时,需要先查看它的参考手册关于字符串的基本操作。不过还好,不同语言除方法名之外,操作实质都是类似的。比如C#语言,字符串操作就还有ToLower转小写,ToUpper转大写,IndexOf从左查找子串位置,LastIndexOf从右查找子串位置,Trim去除两边空格等比较方便的操作,它们其实就是前面这些基本操作的扩展函数。

我们来看一个操作Index 的实现算法。

int Index(String S ,String T,int pos){int n,m,i;String sub;if(pos>0){n=StrLength(S);//得到主串S的长度m=StrLength(T);//得到子串T的长度i=pos;while(i<=n-m+1){SubString(sub,S,i,m);//取主串第i个位置上长度与T相等的子串给subif(StrCompare(sub,T)!=0) ++i;// 如果两串不相等else return i; //如果两串相等}}return 0; //如果子串与T相等,返回0
}

其中用到了StrLength,SubString,StrCompare等基本操作来实现。

5.5 串的存储结构

串的存储结构和线性表相同,分为两种。

5.5.1 串的顺序存储结构

串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的。按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区。一般用定长数组来定义。

既然是定长数组,就存在一个预定义的最大串长度,一般可以将实际的串长度值保存在数组0下标位置,有的书中也会定义存储在数组的最后一个下标位置。但是有些编程语言不想这么干,觉得存个数占空间麻烦。它规定在串值后面加一个不计入串长度的结束标记字符,比如“\0”来表示串值的终结,这个时候,你想要知道此时的串长度,就需要遍历计算一下才知道,其实这还是需要占用一个空间,何必呢。

刚才讲的串的顺序存储结构其实是有问题的,因为字符串的操作,比如两串的连接Concat,两串的插入StrInsert,以及字符串的替换Replace,都有可能使得串序列的长度超过了数组的长度MaxSize。

于是对于串的顺序存储,有一些变化,串值的存储空间可在程序执行过程中动态分配而得。比如在计算机中存在一个自由存储区,叫做“堆”。这个堆可由C语言的动态分配函数malloc()和free()来管理。

5.5.2 串的链式存储结构

对于串的链式存储结构,与线性表是相似的,但由于串结构的特殊性,结构中的每个元素数据是一个字符,如果也简单的应用链表存储串值,一个结点对应一个字符,就会存在很大的空间浪费。因此,一个结点可以存放一个字符,也可以考虑存放多个字符,最后一个结点若是未被占满时,可以用“#”或其他非串值字符补全,如图所示

当然,这里一个结点存多少个字符才合适就变得很重要,这会直接影响着串处理的效率,需要根据实际情况做出选择。

但串的链式存储结构除了在连接串与串操作时有一定方便之外,总的来说不如顺序存储灵活,性能也不如顺序存储结构好。

5.6朴素的模式匹配算法

在计算机文献中常用的英文单词有哪些呢?想写个程序,只要输入一些英文的文档,就可以计算出这当中所用频率最高的词汇是哪些。当然,实际操作时有很多困难,不过,这里面最重要其实就是去找一个单词在一篇文章(相当于一个大字符串)中的定位问题。这种子串的定位操作通常称作串的模式匹配,应该算是串中最重要的操作之一。

假设我们要从下面的主串S="goodgoogle"中,找到T="google"这个子串的位置。我们通常需要下面的步骤。

1 主串S第一位开始,S与T前三个字母都匹配成功,但S的第四个字母是d而T的字母是。第一位匹配失败。如下图所示,其中竖直连线表示相等,闪电状弯折线表示不等。

2 主串S第二位开始,主串S首字母是o,要匹配的T首字母是g,匹配失败。如下图所示

3 主串S第三位开始,主串S首字母是o,要匹配的T首字母是g,匹配失败,如下图所示

4 主串S第四位开始,主串S首字母是d,而T首字母是g,匹配失败,如下图

5 主串S第五位开始,S与T,6个字母全匹配,匹配成功,如下图所示

简单地说,就是对主串的每个字符作为子串的开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做T的长度的小循环,直到匹配成功或全部遍历完成为止。

前面我们已经用串的其他操作实现了模式匹配算法Index。现在考虑不用串的其他操作,而是只用基本的数组来实现同样的算法。注意我们假设主串S和要匹配的子串T的长度存在S[0]和T[0]中,实现的代码如下

//返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0int Index(String S,String T,int pos){int i=pos;int j=1;while(i<=S[0]&&j<=T[0]){if(S[i]==T[j]) ++i,++j;else{指针后退重新开始匹配i=i-j+2;//i回退到上次匹配首位的下一位j=1;//j回退到子串T的首位}}if(j>T[0]) return i-T[0];//T[0]是子串的长度else return 0;}

分析一下,最好的情况是什么?那就是一开始就匹配成功,时间复杂度为O(1)。稍差一些,就像第二、三位一样,每次都是首字母不匹配,那么对T串的循环就不必进行了,比如“abcdef”里面找"goo"。那么时间复杂度为O(n+m),其中n为主串长度,m为要匹配的字串长度。一般情况,根据等概率原则,平均是m+n2\frac{m+n}{2}2m+n​次查找,时间复杂度为O(m+n).

那么最坏的情况呢? 就是每次不成功的匹配都发生在串T的最后一个字符。举一个极端的例子。主串S为"00…000001",而要匹配的子串T为"0000000001",前者是有49个0和1个1的主串,后者是9个0和1个1的子串。在匹配时,每次都得将T中字符循环到最后一位才发现:哦,原来它们不匹配啊。这样等于T串需要在S串的前40个位置都需要判断10次,并得出不匹配的结论,如下图所示

直到最后第41个位置,因为全部匹配成功,所以不需要再继续下去,如下图所示。如果最终没有可匹配的子串,比如T是“00000000002”,到了第41个位置判断不匹配后同样不需要继续比对下去。因此最坏情况的时间复杂度是O((n-m+1)*m)。

不要以为这是很少见的情况,相反,在实际运用中,对于计算机来说,处理的都是二进制位的01串,一个字符的ASCII码也可以看成是8位的二进制位01串,当然,汉字等所有的字符也都可以看成是多个01串。再比如像计算机图形也可以理解成是由许许多多个0和1的串组成的。所以在计算机的运算当中,模式匹配操作可以说是随处可见,而刚才的这个算法,就显得太低效率。

5.7 KMP模式匹配算法

在很多年前,我们的科学家们,觉得像这种有多个0和1重复字符的字符串,却需要挨个遍历的算法是非常糟糕的事情。于是有三位前辈,D.E.Knuth,J.H.Morris 和V.R.Pratt 发表一个模式匹配算法,可以大大避免重复遍历的情况,我们把它称为克努特-莫里斯-普拉特算法,简称KMP算法。

(未完待续)

5.7.1 KMP模式匹配算法原理

5.7.2 next数组值推导

5.7.3 KMP模式匹配算法实现

5.7.4 KMP模式匹配算法改进

5.7.5 nextval数组值推导

《大话数据结构》读书笔记-串相关推荐

  1. 大话数据结构读书笔记艾提拉总结 查找算法 和排序算法比较好 第1章数据结构绪论 1 第2章算法 17 第3章线性表 41 第4章栈与队列 87 第5章串 123 第6章树 149 第7章图 21

    大话数据结构读书笔记艾提拉总结 查找算法 和排序算法比较好 第1章数据结构绪论 1 第2章算法 17 第3章线性表 41 第4章栈与队列 87 第5章串 123 第6章树 149 第7章图 211 第 ...

  2. 大话数据结构读书笔记系列(五)串

    2019独角兽企业重金招聘Python工程师标准>>> 5.1 开场白 同学们,大家好!我们开始上新的一课. 我们古人没有电影电视,没有游戏网络,所以文人们就会想出一些文字游戏来娱乐 ...

  3. 大话数据结构读书笔记系列(七)图

    2019独角兽企业重金招聘Python工程师标准>>> 7.1 开场白 旅游几乎是每个年轻人的爱好,但没有钱或没时间也是困惑年轻人不能圆梦的直接原因.如果可以用最少的资金和最少的时间 ...

  4. 大话数据结构读书笔记系列(三)线性表

    2019独角兽企业重金招聘Python工程师标准>>> 3.1 开场白 各位同学,大家好.今天我们要开始学习数据结构中最常用和最简单的一种结构,在介绍它之前先讲个例子. 我经常下午去 ...

  5. [大话数据结构-读书笔记] 栈

    栈 1 栈的定义 1.1 栈的定义 在我们软件应用中,栈这种后进先出数据结构的应用是非常普遍的.比如Word. Photoshop 等文档或图像编辑软件中, 都有撤销 (undo)的操作,也是用栈这种 ...

  6. 大话设计模式读书笔记

    主题 概要 设计模式 大话设计模式读书笔记 编辑 时间 新建 20170423 序号 参考资料 1 大话设计模式 重新看了一遍设计模式,除了一些已经特别熟悉的模式,都自己敲了一遍代码,有些豁然开朗的感 ...

  7. 《大话数据结构》笔记——第8章 查找(二)

    文章目录 8.6 二叉排序树 8.6.1 二叉排序树查找操作 8.6.2 二叉排序树插入操作 8.6.3 二叉排序树删除操作 8.6.4 二叉排序树总结 8.7 平衡二叉树( AVL树 ) 8.7.1 ...

  8. 《大话数据结构》笔记——第8章 查找(一)

    文章目录 8.1 开场白 8.2 查找概述 8.3 顺序查找 8.3.1 顺序表查找算法 8.3.2 顺序表查找优化 8.4 有序表查找 8.4.1 折半查找 8.4.2 插值查找 8.4.3 斐波那 ...

  9. 《大话数据结构》笔记

    数据结构:相互之间存在一种或多种特定关系的数据元素的集合. 数据的存储结构分为:顺序存储结构,链式存储结构 数据的逻辑结构分为:集合结构,线性结构(一对一),树形结构(一对多),图形结构 (多对多) ...

最新文章

  1. HTML5路由和PJAX
  2. 一个基于 Spring Boot 的项目骨架,拿走即用
  3. 瑞星linux u盘引导盘杀毒教程,瑞星杀毒U盘怎样用U盘启动电脑
  4. [Oracle] CPU/PSU补丁安装教程
  5. PMP之项目质量管理---项目质量管理措施
  6. 线切割机上的DOS系统
  7. 指令系统——数据存放、指令寻址(详解)
  8. 安装nagios_grapher,有图有真相。
  9. 服务器芯片组c621,三路千兆LAN:GIGABYTE 技嘉 发布 C621-SU8 服务器主板
  10. python自动化测试-【自动化测试】Python - unittest单元测试框架
  11. android开发歌词滑动效果_一些Flutter开发中的“坑”
  12. r语言ggplot画两条曲线_如何用R语言绘制生存曲线?
  13. 曲线运动与万有引力公式_高中物理公式:曲线运动公式
  14. 舱机器人尾巴毛茸茸_第一千五百二十四章 毛茸茸【二合一】
  15. 黑客游戏(强烈推荐)攻防ctf
  16. html5给页面添加雨滴特效,JavaScript canvas实现雨滴特效
  17. AIME数学竞赛信息大全
  18. java Virtual Machine Launcher
  19. h3c交换机端口加入vlan命令_7.2.2 H3C交换机VLAN接口基本属性配置
  20. [计算机漫谈]网络初步:一个分组的生命历程

热门文章

  1. 工作四年的php程序员自述 一路走过来的风风雨雨
  2. iOS:app直播---采集篇
  3. (笔记)Mysql命令grant on:增加新用户并控制其权限
  4. 当向后台插入或读取JSON数据遇见回车时
  5. 设计模式学习笔记——目录
  6. 【伯乐在线】程序员常有,优秀程序员不常有
  7. MFC遍历窗体所有的控件
  8. 10个开源的PHP网站内容管理系统
  9. 用实验验证神经网络的节点是否可以看作弹性小球
  10. python判断哪个数最小_怎么用python比较三个数大小