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

5.1 开场白

同窗们,你们好!咱们开始上新的一课。算法

咱们古人没有电影电视,没有游戏网络,因此文人们就会想出一些文字游戏来娱乐。好比宋代的李禺写了这样一首诗:"枯眼望遥山隔水,往来曾见几心知?壶空怕酌一杯酒,笔下难成和韵诗。途路阻人离别久,迅音无雁寄回迟。孤灯夜守长寥寂,夫忆妻兮父忆儿。"显然这个老公想念老婆和儿子的诗句。曾经和妻儿在一块儿,尽享天伦之乐,如今一我的长久没有回家,也不见书信返回,望着油灯想念亲人,能不伤感吗?编程

可再仔细一读发现,这首诗居然能够倒过来读:"儿忆父兮妻忆夫,寂寥长守夜灯孤。迟回寄雁无音讯,久别离人阻路途。诗韵和成难下笔,酒杯一酌怕空壶。知心几见曾来往,水隔山遥望眼枯。"这表达了什么意思呢?呵呵,表达了妻子对丈夫的思念。老公离开很久,路途遥远,难以相见。写信不知道写什么,独自喝酒也没什么兴致。只能和儿子夜夜守在家里一盏孤灯下,苦等老公的归来。数组

这种诗体叫作回文诗。它是一种能够倒读或反复回旋阅读的诗体。刚才这首就是正读是丈夫思念妻子,倒读是妻子思念丈夫的古诗。是否是感受很奇妙呢?网络

在英语单词中,一样有神奇的地方。"即便是lover也有个over,即便是friend也有个end,即便是believe也有个lie"。你会发现,原本不相干,甚至对立的两个词,却有某种神奇的联系。这多是创造这几个单词的那么智者们也没有想到的问题。数据结构

今天咱们就要来谈谈这些单词或句子组成字符串的相关问题。app

5.2 串的定义

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

好比咱们如今经常使用的搜索引擎,当咱们在文本框中输入"数据"时,它已经把咱们想要的"数据结构"列在下面了。显然这里网站做了一个字符串查找匹配的工做,如图5-2-1所示。 函数

今天咱们就是来研究"串"这样的数据结构。先来看定义。性能

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

通常记为s="a1a2...an"(n>0),其中,s是串的名称,用双引号(有些书中也用单引号)括起来的字符序列是串的值,注意单引号不属于串的内容。ai(1<=i<=n)能够是字母、数字或其余字符,i就是该字符在串中的位置。串中的字符数目n称为串的长度,定义中谈到"有限"是指长度n是一个有限的数值。零个字符的串称为空串(null string),它的长度为零,能够直接用两双引号""""表示,也能够用希腊字母"ø"来表示。所谓的序列,说明串的相邻字符之间具备前驱和后继的关系。

还有一些概念须要解释。

  • 空格串,是只包含空格的串。注意它与空串的区别,空格串是有内容有长度的,并且能够不止一个空格。
  • 子串与主串,串中任意个数的连续字符组成的子序列称为该串的子串,相应地,包含子串的串称为主串。
  • 子串在主串中的位置就是子串的第一个字符在主串中的序号。
  • 开头我所提到的"over"、"end"、"lie"其实能够认为是"lover"、"friend"、"believe"这些单词字符串的子串。

5.3 串的比较

两个数字,很容易比较大小。2比1大,这彻底正确,但是两个字符串如何比价?好比"silly"、"stupid"这样的一样表达"愚蠢的"的单词字符串,它们在计算机中的大小其实取决于它们挨个字母的先后顺序。它们的第一个字母都是"s",咱们认为不存在大小差别,而第二个字母,因为"i"字母比"t"字母要靠前,因此"i"<"t",因而咱们说"silly"<"stupid"。

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

计算机中的经常使用字符是使用标准的ASCII编码,更准确一点,由7位二进制数表示一个字符,总共能够表示256个字符,这已经足够知足以英语为主的语言的特殊符号进行输入、存储、输出等操做的字符须要了。但是,单咱们国家就有除汉族外的满、回、藏、蒙古、维吾尔等多个少数民族文字,换做全世界估计要有成百上千种语言与文字,显然这256个字符是不够的,所以后来就有了Unicode编号,比较经常使用的是由16位的二进制表示一个字符,这样总共就能够表示216个字符,约是65万多个字符,足够表示世界上全部语言的全部字符了。固然,为了和ASCII码兼容,Unicode的前256个字符与ASCII码彻底相同。

因此若是咱们要在C语言中比较两个串是否相等,必须是它们串的长度以及它们各个对应位置的字符都相等时,才算是相等。即给定两个串:s="a1a2...an",t="b1b2...bm",当且仅当n=m,且a1=b1,a2=b2,...,an=bm时,咱们认为s=t

那么对于两个串不相等时,如何断定它们的大小呢。咱们这样定义:

  1. n<m,且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。 例如当s="happen",t="happy",由于两串的前4个字母均相同,而两串第5个字母(k值),字母e的ASCII码是101,而字母y的ASCII码是121,显然e<y,因此s<t。(>)

有同窗若是对这样的数学定义很不爽的话,那我再说一个字符串比较的应用。

咱们的英语词典,一般都是上万个单词的有序排列。就大小而言,前面的单词比后面的要小。你在查找单词的过程,其实就是在比较字符串大小的过程。

5.4 串的抽象数据类型

串的逻辑结构和线性表很类似,不一样之处在于串针对的是字符集,也就是串中的元素都是字符,哪怕串中的字符是"123"这样的数字组成,或者"2010-10-10"这样的日期组成,它们都只能理解为长度为3和长度为10的字符串,每一个元素都是字符而已。

所以,对于串的基本操做与线性表是有很大差异的,线性表更关注的是单个元素的操做,好比查找一个元素,插入或删除一个元素,但串中更多的是查找子串位置、获得指定位置子串、替换子串等操做

对于不一样的高级语言,其实对串的基本操做会有不一样的定义方法,因此同窗们在用某个语言操做字符串时,须要先查看它的参考手册关于字符串的基本操做有哪些。不过还好,不一样语言除方法名称外,操做实质都是相相似的。好比C#中,字符串操做就还有ToLower转小写、ToUpper转大写、IndecOf从左查找子串位置(操做名有修改)、LastIndexOf从右查找子串位置、Trim去除两边空格等比较方便的操做,他们其实就是前面这些基本操做的扩展函数。

咱们来看一个操做Index的实现算法。

当中用到了StrLength、SubString、StrCompare等基本操做来实现。

5.5 串的存储结构

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

5.5.1 串的顺序存储结构

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

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

刚才讲的串的顺序存储方式实际上是有问题的,由于字符串的操做,好比两串的链接Concat、新串的插入StrInsert,以及字符串的替换Replace。都有可能使得串序列的长度超过了数组的长度MaxSize。

说说我当年的一件囧事。手机发送短信时,运营商规定每条短信限制70个字。大约八年前,个人手机每当学了超过70个字后,它就提示"短信过长,请删减后重发"。后来我换了一个手机后再也没有这样见鬼的提示了,我很高兴。一次,由于一点小矛盾须要向当时的女朋友解释一下,我准备发一条短信,一共打了79个字。最后的部分字实际上"......只会说好听的话,像'我恨你'这种话是不可能的"。点发送。后来得知对方收到的,只有70个字,短信结尾是"......只会说好听的话,像'我恨你'"。

有这样截断的吗?我后来知道这个状况后,巴不得把手机砸了。显然,不管是上溢提示报错,仍是对多出来的字符串截尾,都不是什么好办法。但字符串操做中,这种状况比比皆是。

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

串的链式存储结构

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

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

但串的链式存储结构除了在链接串与串操做时有必定方便以外,总的来讲不如顺序存储灵活,性能也不如顺序存储结构好。

5.6 朴素的模式匹配算法

记得我在刚作软件开发的时候,须要阅读一些英文的文章或帮助。此时才发现学习英语不仅是为了过四六级,工做中它仍是挺重要的。而我那只为应付考试的英语,早已经忘得差很少了。因而我想在短期内突击一些,很明显,找一本词典从头开始背不是什么好的办法。要背也得背那些最经常使用的,至少是计算机文献中经常使用的,因而我就想本身写一个程序,只要输入一些英文的文档,就能够计算出这当中所用频率最高的词汇是哪些。把它们都背好了,基本上阅读也就不成问题了。

固然,说说容易,要实现这一需求,当中会有不少困难,有兴趣的同窗,不妨去试试看。不过,这里面最重要其实就是去找一个单词咋一篇文章(至关于一个大字符串)中的定位问题。这种子串的定位操做一般称作串的模式匹配,应该算是串中最重要的操做之一。

假设咱们要从下面的主串S="goodgoogle"中,找到T="google"这个子串的位置。咱们一般须要下面的步骤。

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

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

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

  4. 主串S第四位开始,主串S首字母是d,要匹配的T首字母是g,匹配失败,如图5-6-4所示。

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

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

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

分析一下,最好的状况是什么?那就是一开始就匹配成功,好比"googlegood"中去找"google",时间复杂度为O(1)。稍差一些,若是像刚才例子中第2、3、四位同样,每次都是首字符就不匹配,那么对T串的循环就没必要进行了,好比"abcdefgoogle"中去找"google"。那么时间复杂度为O(n+m),其中n为主串长度,m为要匹配的子串长度。根据等几率原则,平均是(n+m)/2次查找,时间复杂度为O(n+m)。

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

直到最后第41个位置,由于所有匹配相等,因此不须要再继续进行下去,如图5-6-7所示。若是最终没有可匹配的子串,好比是T="0000000002",到了第41位置判断不匹配后一样不须要继续比对下去。所以最坏状况的时间复杂度为O((n-m+1)*m)。

不要觉得我这只是危言耸听,在实际运用中,对于计算机来讲,处理的都是二进位的0和1的串,一个字符的ASCII吗也能够当作是8位的二进位01串,固然,汉字等全部的字符也均可以当作是多个0和1串。再好比像计算机图形也能够理解为是由许许多多个0和1的串组成。因此在计算机的运算当中,模式匹配操做可说是随处可见,而刚才的这个算法,就显得过低效了。

5.7 KMP模式匹配算法

大家能够忍受朴素模式匹配算法的低效吗?也许不能够、也许无所谓。但在不少年前咱们的科学家们,以为像这种有多个0和1重复字符的字符串,却须要挨个遍历的算法是很是糟糕的事情。因而有三位前辈,D.E.Knuth、J.H.Morris和V.R.Pratt(其中Knuth和Pratt共同研究,Morris独立研究)发表一个模式匹配算法,能够大大避免重复遍历的状况,咱们把它称之为克努特-莫里斯-普拉特算法,简称KMP算法

5.7.1 KMP模式匹配算法原理

为了能讲清楚KMP算法,咱们不直接讲代码,那样很容易形成理解困难,仍是从这个算法的研究角度来理解为何它比朴素算法要好。

若是主串S="abcdefgab",其实还能够更长一些,咱们就省略掉只保留前9位,咱们要匹配的T="abcdex",那么若是用前面的朴素算法的话,前5个字母,两个串彻底相等,直到第6个字母,"f"与"x"不等,如图5-7-1的(1)所示。

接下来,按照朴素模式匹配算法,应该是如图5-7-1的流程(2)(3)(4)(5)(6)。即主串中当i=2,3,4,5,6时,首字符与子串T的首字符均不等。

彷佛这也是理所固然,原来的算法就是这样设计的。可仔细观察发现。对于要匹配的子串T来讲,"abcdex"首字母"a"与后面的串"bcdex"中任意一个字符都不相等。也就是说,既然"a"不与本身后面的子串中任何一字符相等,那么对于图5-7-1的(1)来讲,前五位字符分别相等,意味着子串T的首字符"a"不可能与S串的第2位到第5位的字符相等。在图5-7-1中,(2)(3)(4)(5)的判断都是多余。

注意这里是理解KMP算法的关键。若是咱们知道T串中首字符"a"与T中后面的字符均不相等(注意这是前提,如何判断后面再讲)。而T串的第二位的"b"与S串中第二位的"b"在图5-7-1的(1)中已经判断是相等的,那么也就意味着,T串中首字符"a"与S串中的第二位"b"是不须要判断也知道它们是不可能相等了,这样图5-7-1的(2)这一步判断是能够省略的,如图5-7-2所示。

一样道理,在咱们知道T串中首字符"a"与T中后面的字符均不相等的前提下,T串的"a"与S串后面的字符均不相等的前提下,T串的"a"与S串后面的"c","d","e"也均可以在(1)以后就能够肯定是不相等的,因此这个算法当中(2)(3)(4)(5)没有必要,只保留(1)(6)便可,如图5-7-3所示。

之因此保留(6)中的判断是由于在(1)中T[6]!=S[6],尽管咱们已经知道T[1]!=T[6],但也不能判定T[1]必定不等于S[6],所以须要保留(6)这一步。

有人就会问,若是T串后面也含有首字符"a"的字符怎么办呢?

咱们来看下面一个例子,假设S="abcabcabc",T="abcabx"。对于开始的判断,前5个字符彻底相等,第6个字符不等,如图5-7-4的(1)。此时,根据刚才的经验,T的首字符"a"与T的第二位字符"b",第三位字符"c"均不等,因此不须要作判断,图5-7-4的朴素算法步骤(2)(3)都是多余。

由于T的首位"a"与T第四位的"a"相等,第二位的"b"与第五位的"b"相等。而在(1)时。第四位的"a"与第五位的"b"已经与主串S中的相应位置比较过了,是相等的,所以能够判定,T的首字符"a"、第二位的字符"b"与S的第四位字符和第五位字符也不须要比较了,确定也是相等的--以前比较过了,还判断什么,因此(4)(5)这两个比较得出字符相等的步骤也能够省略。

也就是说,对于在子串中有与首字符相等的字符,也是能够省略一部分没必要要的判断步骤。如图5-7-5所示,省略掉右图的T串前两位"a"与"b"同S串中的四、5位置字符匹配操做

对比这两个例子,咱们会发如今(1)时,咱们的i值,也就是主串当前位置的下标是6,(2)(3)(4)(5),i值是2,3,4,5,到了(6),i值才又回到了6。即咱们在朴素的模式匹配算法中,主串的i值是不断地回溯来完成的。而咱们的分析发现,这种回溯实际上是能够不须要的--正所谓好马不吃回头草,咱们的KMP模式匹配算法就是为了让这不必的回溯不发生。

既然i值不回溯,也就是不能够变小,那么要考虑的变化就是j值了。经过观察也可发现,咱们屡屡提到了T串的首字符与自身后面字符的比较,发现若是有相等字符,j值的变化就会不相同。也就是说,这个j值的变化与主串其实没什么关系,关键就取决于T串的结构中是否有重复的问题。

好比图5-7-3中,因为T="abcdex",当中没有任何重复的字符,因此j就由6变成了1.而图5-7-4中,因为T="abcabx",前缀的"ab"与最后"x"以前串的后缀"ab"是相等的。所以j就由6变成了3.所以,咱们能够得出规律,j值的多少取决于当前字符之间的串的先后缀的类似度。

咱们把T串各个位置的j值的变化定义为一个数组next,那么next的长度就是T串的长度。因而咱们能够获得下面的函数定义:

5.7.2 next数组推导

具体如何推导出一个串的next数组值呢,咱们来看一些例子。

  1. T="abcdex"(如表5-7-1所示)
  • 1)当j=1时,next[1]=0;
  • 2)当j=2时,j由1到j-1就只有字符"a",属于其余状况next[2]=1;
  • 3)当j=3时,j由1到j-1串是"ab",显然"a"与"b"不相等,属其余状况,next[3]=1;
  • 4)之后同理,因此最终此T串的next[j]为011111。
  1. T="abcabx"(如图5-7-2所示)
  • 1)当j=1时,next[1]=0;
  • 2)当j=2时,同上例说明,next[2]=1;
  • 3)当j=3时,同上,next[3]=1;
  • 4)当j=4时,同上,next[4]=1;
  • 5)当j=5时,此时j由1到j-1的串是"abca",前缀字符"a"与后缀字符"a"相等,所以可推算出k值为2(由'p1...p(k-1)'='p(j-k+1)...p(j-1)',获得p1=p4),所以next[5]=2;
  • 6)当j=6时,j由1到j-1的串是"abcab",因为前缀字符"ab"与后缀"ab"相等,因此next[6]=3。

咱们能够根据经验获得若是先后缀一个字符相等,k值是2,两个字符k值是3,n个字符相等k值就是n+1。

  1. T="ababaaaba"(如表5-7-3所示)
  • 1)当j=1时,next[1]=0;
  • 2)当j=2时,同上next[2]=1;
  • 3)当j=3时,同上next[3]=1;
  • 4)当j=4时,j由1到j-1的串是"aba",前缀字符"a"与后缀字符"a"相等,next[4]=2;
  • 5)当j=5时,j由1到j-1的串是"abab",因为前缀字符"ab"与后缀"ab"相等,因此next[5]=3;
  • 6)当j=6时,j由1到j-1的串的"ababa",因为前缀字符"aba"与后缀"aba"相等,因此next[6]=4;
  • 7)当j=7时,j由1到j-1的串是"ababaa",因为前缀字符"ab"与后缀"aa"并不相等,只有"a"相等,因此next[7]=2;
  • 8)当j=8时,j由1到j-1的串是"ababaaa",只有"a"相等,因此next[8]=2;
  • 9)当j=9时,j由1到j-1的串是"ababaaab",因为前缀字符"ab"与后缀"ab"相等,因此next[9]=3。
  1. T="aaaaaaaab"(如表5-7-4所示)
  • 1)当j=1时,next[1]=0;
  • 2)当j=2时,同上next[2]=1;
  • 3)当j=3时,j由1到j-1的串是"aa",前缀字符"a"与后缀字符"a"相等,next[3]=2;
  • 4)当j=4时,j由1到j-1的串是"aaa",因为前缀字符"aa"与后缀"aa"相等,因此next[4]=3;
  • 5)......
  • 6)当j=9时,j由1到j-1的串是"aaaaaaaa",因为前缀字符"aaaaaaa"与后缀"aaaaaaa"相等,因此next[9]=8。

5.7.3 KMP模式匹配算法实现

说了这么多,咱们能够来看看代码了。

这段代码的目的就是为了计算出当前要匹配的串T的next数组。

加粗的为相对于朴素匹配算法增长的代码,改动不算大,关键就是去掉了i值回溯的部分。对于get_next函数来讲,若T的长度为m,因只涉及到简单的单循环,其时间复杂度为O(m),而因为i值的不回溯,使得index_KMP算法效率获得了提升,while循环的时间复杂度为O(n)。所以,整个算法的时间复杂度为O(n+m)。相较于朴素模式匹配算法的O((n-m+1)*m)来讲,是要好一些。

这里也须要强调,KMP算法仅当模式与主串之间存在许多"部分匹配"的状况下才体现出它的优点,不然二者差别并不明显。

5.7.4 KMP模式匹配算法改进

后来有人发现,KMP仍是有缺陷的。好比,若是咱们的主串S="aaaabcde",子串T="aaaaax",其next数组值分别为012345,在开始时,当i=五、j=5时,咱们发现"b"与"a"不相等,如图5-7-6的(1),所以j=next[5]=4,如图中的(2),此时"b"与第4位置的"a"依然不等,j=next[4]=3,如图中的(3),后依次是(4)(5),直到j=next[1]=0时,根据算法,此时i++,j++,获得i=6,j=1,如图中的(6)。

咱们发现,当中的(2)(3)(4)(5)步骤,实际上是多余的判断。因为T串的第2、3、4、五位置的字符都与首位的"a"相等,那么能够用首位next[1]的值去取代与它相等的字符后续next[j]的值,这是个很好的办法。所以咱们对求next函数进行了改良。

假设取代的数组为nextval,增长了加粗部分,代码以下:

实际匹配算法,只须要将"get_next(T,next)";改成"get_nextval(T,next);"便可,这里再也不重复。

5.7.5 nextval数组值推导

改良后,咱们以前的例子nextval值就与next值不彻底相同了。好比:

  1. T="ababaaaba"(如表5-7-5所示)

先算出next数组的值分别是001234223,而后再分别判断。

  • 1)当j=1时。nextval[1]=0;
  • 2)当j=2时,因第二位字符"b"的next值是1,而第一位就是"a",他们不相等,因此nextval[2]=next[2]=1,维持原值。
  • 3)当j=3时,由于第三位字符"a"的next值为1,因此与第一位的"a"比较得知它们相等,因此nextval[3]=nextval[1]=0;如图5-7-7所示。

4)当j=4时,第四位的字符"b",next值为2,因此与第二位的"b"相比较获得结果是相等,所以nextval[4]=nextval[2]=1;如图5-7-8所示。

  • 5)当j=5时,next值为3,第五个字符"a"与第三个字符"a"相等,所以nextval[5]=nextval[3]=0;
  • 6)当j=6时,next值为4,第六个字符"a"与第四个字符"b"不相等,所以nextval[6]=4;
  • 7)当j=7时,next值为2,第七个字符"a"与第二个字符"b"不相等,所以nextval[7]=2;
  • 8)当j=8时,next值为2,第八个字符"b"与第二个字符"b"相等,所以nextval[8]=nextval[2]=1;
  • 9)当j=9时,next值为3,第九个字符"a"与第三个字符"a"相等,所以nextval[9]=nextval[3]=1。
  1. T="aaaaaaaab"(如表5-7-6)

先算出next数组的值分别为012345678,而后再分别判断。

  • 1)当j=1时,nextval[1]=0;
  • 2)当j=2时,next值为1,第二个字符与第一个字符相等,因此nextval[2]=nextval[1]=0;
  • 3)一样的道理,其后都为0....;
  • 4)当j=9时,next值为8,第九个字符"b"与第八个字符"a"不相等,因此nextval[9]=9。

总结改进过的KMP算法,它是在计算出next值的同时,若是a位字符与它next值指向的b位字符相等,则该a位的nextval就指向b位的nextval值,若是不等,则该a位的nextval值就是它本身a位的next的值。

5.8 总结回顾

这一章节咱们重点讲了"串"这样的数据结构,串(string)是由零个或多个字符组成的有限序列,又名字符串。本质上,它是一种线性表的扩展,但相对于线性表关注一个个元素来讲,咱们对串这种结构更多的是关注它子串的应用问题,如查找、替换等操做。如今的高级语言都有针对串的函数能够调用。咱们在使用这些函数的时候,同时也应该要理解它当中的原理,以便于在碰到复杂的问题时,能够更加灵活的使用,好比KMP模式匹配算法的学习,就是更有效地去理解index函数当中的实现细节。多用心一点,说不定有一天,能够有以你的名字命令的算法流传于后世。

相关文章
相关标签/搜索