1、Chapter 4 String,串的定义,n(n=0)个字符的有限序列 S=a1,a2,an串名: s串值: ai(1=i=n)串长: n,术 语,空 串n=0的串 子 串串中若干相邻字符组成的子序列 主 串包含子串的串 空格串仅含有空格字符的串(n不为0) 串相等设 s1=a11,an1s2=a12,an2若 n1=n2且ai1=ai2(1=i=n1)则 s1=s2,串的抽象数据类型,ADT String ,数据对象:,D ai |aiCharacterSet,i=1,2,.,n, n0 ,数据关系:,R1 | ai-1, ai D,i=2,.,n ,基本操作:,StrAssign (&T
2、, chars),StrCopy (&T, S),DestroyString(&S),StrEmpty (S),StrCompare (S, T),StrLength(S),Concat (&T, S1, S2),SubString (&Sub, S, pos, len),Index (S, T, pos),Replace (&S, T, V),StrInsert (&S, pos, T),StrDelete (&S, pos, len),ClearString (&S), ADT String,StrAssign (&T, chars)初始条件:chars 是字符串常量操作结果:把 char
3、s 赋为 T 的值,StrCopy (&T, S)初始条件:串 S 存在操作结果:由串 S 复制得串 T,DestroyString (&S)初始条件:串 S 存在操作结果:串 S 被销毁,StrEmpty(S) 始条件:串S存在 操作结果:若 S 为空串,则返回 TRUE,否则返回 FALSE, 表示空串,空串的长度为零,StrCompare(S,T) 初始条件:串S和T存在 操作结果:若S T,则返回值 0 若S T,则返回值 0 若S T,则返回值 0,例如:StrCompare(data, state) 0,StrLength(S) 初始条件:串 S 存在 操作结果:返回 S 的元素个
4、数, 称为串的长度,Concat(&T,S1,S2) 初始条件:串 S1 和 S2 存在 操作结果:用 T 返回由 S1 和 S2 联接而成的新串,例如: Concate( T, man, kind)求得 T = mankind,SubString (&Sub, S, pos, len) 初始条件:串 S 存在,1posStrLength(S)且0lenStrLength(S)-pos+1 操作结果: 用 Sub 返回串 S 的第 pos 个字符 起长度为 len 的子串,例如:SubString( sub, commander, 4, 3)求得 sub = manSubString( sub
5、, commander, 1, 9) 求得 sub = commanderSubString( sub, commander, 9, 1) 求得 sub = r,子串为“串” 中的一个字符子序列,SubString(sub, commander, 4, 7)sub = ?,SubString(sub, beijing, 7, 2) = ?sub = ?,SubString(student, 5, 0) = ,起始位置和子串长度之间存在约束关系,长度为 0 的子串为“合法”串,Index(S,T,pos) 初始条件:串S和T存在,T是非空串, 1posStrLength(S) 操作结果: 若主串
6、 S 中存在和串 T 值 相同的子串, 则返回它在主 串S中第pos个 字符之后第一次出现的位 置;否则函数值为0,假设 S = abcaabcaaabc, T = bca,Index(S, T, 1) = 2,Index(S, T, 3) = 6,Index(S, T, 8) = 0,“子串在主串中的位置”意指子串 中的第一个字符在主串中的位序,Replace(&S,T,V) 初始条件:串S, T和 V 均已存在, 且 T 是非空串 操作结果:用V替换主串S中出现 的所有与(模式串)T 相等的不重叠的子串,假设 S = abcaabcaaabca,T = bca,若 V = x, 则经置换后
7、得到S = axaxaax,若 V = bc, 则经置换后得到S = abcabcaabc,StrInsert (&S, pos, T) 初始条件:串S和T存在, 1posStrLength(S)1 操作结果:在串S的第pos个字符之前插入串T,例如:S = chater,T = rac,则执行 StrInsert(S, 4, T)之后得到S = character,StrDelete (&S, pos, len) 初始条件:串S存在 1posStrLength(S)-len+1 操作结果:从串S中删除第pos个字符 起长度为len的子串,ClearString(&S) 初始条件:串S存在 操
8、作结果:将S清为空串,在上述抽象数据类型定义的13种操作中 串赋值StrAssign、 等六种操作串复制Strcopy、 构成串类型串比较StrCompare、 的最小操作求串长StrLength、 子集串联接Concat求子串SubString,即: 这些操作不可能利用其他串操作来实现, 反之,其他串操作(除串清除ClearString和串销毁DestroyString外)可在这个最小操作子集上实现,StrCompare(SubString(S,i,StrLength(T),T) ? 0,例如,可利用串比较、求串长和求子串等操作实现定位函数Index(S,T,pos)。,S 串,T 串,T
9、串,i,pos,n-m+1,算法的基本思想为:,int Index (String S, String T, int pos) /T为非空串。若主串S中第pos个字符 之后存在与 T相等的子串,则返回第 一个这样的子串在S中的位置,否则返回0if (pos 0) n = StrLength(S); m = StrLength(T);i = pos;,while ( i = n-m+1) SubString (sub, S, i, m);if (StrCompare(sub,T) != 0) +i ;else return i ; / while / ifreturn 0; / S中不存在与T相
10、等的子串 / Index,又如串的置换函数:,S 串,T 串,V 串,V 串,pos,pos,sub,i,news 串,sub,串的逻辑结构和线性表极为相似,区别仅在于串的数据对象约束为字符集,串的基本操作和线性表有很大差别,在线性表的基本操作中,大多以“单个元素”作为操作对象在串的基本操作中,通常以“串的整体”作为操作对象,在程序设计语言中,串只是作为输入或输出的常量出现,则只需存储此串的串值,即字符序列即可。但在多数非数值处理的程序中,串也以变量的形式出现,串的表示和实现,串的存储结构 对串的存储方式取决于我们对串所进行的运算,如果在程序设计语言中,串的运算只是作为输入或输出的常量出现,则
11、此时只需存储该串的字符序列,这就是串值的存储。此外,一个字符序列还可赋给一个串变量,操作运算时通过串变量名访问串值。实现串名到串值的访问,在C+语言中可以有两种方式:一是可以将串定义为字符型数组,数组名就是串名,串的存储空间分配在编译时完成,程序运行时不能更改。这种方式为串的静态存储结构。另一种是定义字符指针变量,存储串值的首地址,通过字符指针变量名访问串值,串的存储空间分配是在程序运行时动态分配的,这种方式称为串的动态存储结构。,串值的存储 我们称串是一种特殊的线性表,因此串的存储结构表示也有两种方法:静态存储采用顺序存储结构,动态存储采用的是链式存储和堆存储结构。,1串的静态存储结构 类似
12、于线性表的顺序存储结构,用一组地址连续的存储单元存储串值的字符序列。由于一个字符只占1个字节,而现在大多数计算机的存储器地址是采用的字编址,一个字(即一个存储单元)占多个字节,因此顺序存储结构方式有两种: (1)紧缩格式:即一个字节存储一个字符。这种存储方式可以在一个存储单元中存放多个字符,充分地利用了存储空间。但在串的操作运算时,若要分离某一部分字符时,则变得非常麻烦。,图4-1 串值的紧缩格式存储,图4-1所示是以4个字节为一个存储单元的存储结构,每个存储单元可以存放4个字符。对于给定的串s=datastructure,在C+语言中采用字符0作串值的结束符。串s的串值连同结束符的长度共15
13、,只需4个存储单元。,串值的紧缩格式存储,由上述讨论可知,串的顺序存储结构有两大不足之处:一是需事先预定义串的最大长度,这在程序运行前是很难估计的。二是由于定义了串的最大长度,使得串的某些操作受限,如串的联接运算等。,(2)非紧缩格式:这种方式是以一个存储单元为单位,每个存储单元仅存放一个字符。这种存储方式的空间利用率较低,如一个存储单元有4个字节,则空间利用率仅为25%。但这种存储方式中不需要分离字符,因而程序处理字符的速度高。,2串的动态存储结构 我们知道,串的各种运算与串的存储结构有着很大的关系,在随机取子串时,顺序存储方式操作起来比较方便,而对串进行插入、删除等操作时,就会变得很复杂。
14、因此,有必要采用串的动态存储方式。 串的动态存储方式采用链式存储结构和堆存储结构两种形式: (1)链式存储结构 串的链式存储结构中每个结点包含字符域和结点链接指针域,字符域用于存放字符,指针域用于存放指向下一个结点的指针,因此,串可用单链表表示。 用单链表存放串,每个结点仅存储一个字符,因此,每个结点的指针域所占空间比字符域所占空间要大得多。为了提高空间的利用率,我们可以使每个结点存放多个字符,称为块链结构每个结点存放4个字符。,(2)堆存储结构 堆存储结构的特点是,仍以一组空间足够大的、地址连续的存储单元存放串值字符序列,但它们的存储空间是在程序执行过程中动态分配的。每当产生一个新串时,系统
15、就从剩余空间的起始处为串值分配一个长度和串值长度相等的存储空间。,串名的存储映象串名的存储映象就是建立了串名和串值之间的对应关系的一个符号表。在这个表中的项目可以依据实际需要来设置,以能方便地存取串值为原则。 如: s1=data s2=structure若符号表中每行包含有串名、串值的始地址、尾地址,也可以不设尾地址,而设置串和长度值。,对于链式存储串值的方式,如果要建立串变量的符号表,则只需要存入一个链表的表头指针即可。,串的定长顺序存储表示,串的堆分配存储表示,串的块链存储表示,#define MAXSTRLEN 255/ 用户可在255以内定义最大串长typedef unsigned
16、char SstringMAXSTRLEN + 1;/ 0号单元存放串的长度,串的定长顺序存储表示,按这种串的表示方法实现的串的运算时,其基本操作为 “字符序列的复制”,串的实际长度可在这个予定义长度的范围内随意设定,超过予定义长度的串值则被舍去,称之为 “截断”,特点:,例 如 串的联接算法中 需分三种情况处理,Concat(SString S1, SString S2, SString / Concat,T1S10 = S11S10;TS10+1S10+S20 = S21S20;T0 = S10+S20; uncut =TRUE; ,if(S10+S20=MAXSTRLEN)/未截断,el
17、se if (S10 MAXSTRSIZE) / 截断,else / 截断(仅取S1),T1S10 = S11S10; TS10+1MAXSTRLEN =S21MAXSTRLENS10; T0=MAXSTRLEN;uncut = FALSE;,T0MAXSTRLEN = S10MAXSTRLEN; T0 = S10 = MAXSTRLEN; uncut = FALSE; ,clsss Stringchar *ch; / 若是非空串,则按串长分/ 配存储区,否则ch为NULLint length; / 串长度,串的堆分配存储表示,Concat(String ,S.length = S1.leng
18、th + S2.length; S.ch 0S1.length-1 =S1.ch 0S1.length-1 ; S.ch S1.lengthS.length-1 = S2.ch 0S2.length-1 ; return OK; / Concat,SubString( String ,if (Sub.ch) delete Sub.ch; / 释放旧空间if ( !len ) / 空子串 Sub.ch = NULL; Sub.length = 0; else Sub.ch = new charlen;Sub.ch0len-1 = Spos-1pos+len-2;Sub.length = len;
19、 / 完整子串return OK; / SubString,串的块链存储表示,也可用链表来存储串值,由于串的数据元素是一个字符,它只有8 位二进制数,因此用链表存储时,通常一个结点中存放的不是一个字符,而是一个子串,存储密度 =,数据元素所占存储位,实际分配的存储位,例如: 在编辑系统中,整个文本编辑区可以看成是一个串,每一行是一个子串,构成一个结点。即: 同一行的串用定长结构(80个字符), 行和行之间用指针相联接,实际应用时,可以根据问题所需来设置结点的大小,这是串的一种重要操作,很多软件,若有“编辑”菜单项的话,则其中必有“查找”子菜单项,串的模式匹配算法,简单算法,首尾匹配算法,KMP
20、(D.E.Knuth, V.R.Pratt,J.H.Morris) 算法,int Index(SString S, SString T, int pos) / 返回子串T在主串S中第pos个字符/ 之后的位置。若不存在,则函数值/ 为0。其中,T非空,/ 1posStrLength(S)i = pos; j = 1;,简单算法,while (i T0) return i-T0;else return 0; / Index,串的模式匹配,定义 在串中寻找子串(第一个字符)在串中的位置 词汇 在模式匹配中,子串称为模式,串称为目标。 示例 目标 T : “Beijing”模式 P : “jin”匹
21、配结果 = 3,第1趟 T a b b a b a 穷举的模式P a b a 匹配过程第2趟 T a b b a b aP a b a 第3趟 T a b b a b aP a b a第4趟 T a b b a b aP a b a,int String:Find ( String ,目标 T t0 t1 t2 tm-1 tn-1 模式 pat p0 p1 p2 pm-1目标 T t0 t1 t2 tm-1 tm tn-1 模式 pat p0 p1 pm-2 pm-1目标 T t0 t1 ti ti+1 ti+m-2 ti+m-1 tn-1 模式 pat p0 p1 pm-2 pm-1,改进的
22、模式匹配,穷举的模式匹配算法时间代价:最坏情况比较n-m+1趟,每趟比较m次,总比较次数达(n-m+1)*m原因在于每趟重新比较时,目标串的检测指针要回退。改进的模式匹配算法可使目标串的检测指针每趟不回退。改进的模式匹配(KMP)算法的时间代价:若每趟第一个不匹配,比较n-m+1趟,总比较次数最坏达(n-m)+m = n若每趟第m个不匹配,总比较次数最坏亦达到 n,T t0 t1 ts-1 ts ts+1 ts+2 ts+j-1 ts+j ts+j+1 tn-1 P p0 p1 p2 pj-1 pj pj+1则有 ts ts+1 ts+2 ts+j = p0 p1 p2 pj (1)为使模式
23、P 与目标 T 匹配,必须满足p0 p1 p2 pj-1 pm-1 = ts+1 ts+2 ts+3 ts+j ts+m如果 p0 p1 pj-1 p1 p2 pj (2)则立刻可以断定p0 p1 pj-1 ts+1 ts+2 ts+j下一趟必不匹配,同样,若 p0 p1 pj-2 p2 p3 pj则再下一趟也不匹配,因为有p0 p1 pj-2 ts+2 ts+3 ts+j直到对于某一个“k”值,使得p0 p1 pk+1 pj-k-1 pj-k pj 且 p0 p1 pk = pj-k pj-k+1 pj则 p0 p1 pk = ts+j-k ts+j-k+1 ts+j pj-k pj-k+1
24、 pj,k 的确定方法当比较到模式第 j 个字符失配时, k 的值与模式的前 j 个字符有关,与目标无关。利用失效函数 f (j)可描述。利用失效函数 f (j) 的匹配处理如果 j = 0,则目标指针加 1,模式指针回到 p0。如果 j 0,则目标指针不变,模式指针回到 pf(j-1)+1。,若设 模式 P = p0 p1pm-2 pm-1,示例 :确定失效函数 f (j),运用KMP算法的匹配过程第1趟 目标 a c a b a a b a a b c a c a a b c模式 a b a a b c a c j = 1 j = f (j-1)+1 = 0 第2趟 目标 a c a b
25、a a b a a b c a c a a b c模式 a b a a b c a c j = 5 j = f (j-1)+1= 2 第3趟 目标 a c a b a a b a a b c a c a a b c模式 (a b) a a b c a c,int String : fastFind ( String pat ) const /带失效函数的KMP匹配算法int posP = 0, posT = 0; int lengthP = pat.curLen, lengthT = curLen; while ( posP lengthP ,计算失效函数 f j 的方法首先确定f 0 = -
26、1,再利用f j求f j+1。其中, f (1) j = f j ,f (m) j = f f (m -1) j ,f 0 = -1; j = 1时, f 0+1 = 0, p0 p1, f 1 = -1; j = 2时, f 1+1 = 0, p0 = p2, f 2 = f 1+1 = 0; j = 3时, f 2+1 = 1, p1 p3, f 1+1= 0, p0 = p3, f 3 = f 1+1 = 0; j = 4时, f 3+1 = 1, p1 = p4, f 4 = f 3+1 = 1;,void String:fail ( ) /计算失效函数 int lengthP = c
27、urLen;f 0 = -1; /直接赋值for ( int j=1; j= 0 )i = f i; /递推if ( *(ch+j) = *(ch+i+1) ) f j = i+1;else f j = -1; ,文本编辑 文本编辑是串的一个很典型的应用。它被广泛用于各种源程序的输入和修改,也被应用于信函、报刊、公文、书籍的输入、修改和排版。文本编辑的实质就是修改字符数据的形式或格式。在各种文本编辑程序中,它们把用户输入的所有文本都作为一个字符串。尽管各种文本编辑程序的功能可能有强有弱,但是它们的基本的操作都是一致的,一般包括串的输入、查找、修改、删除、输出等。,例如有下列一段源程序: mai
28、n() int a,b,c; scanf(%d,%d, 我们把这个源程序看成是一个文本,为了编辑的方便,总是利用换行符把文本划分为若干行,还可以利用换页符将文本组成若干页,这样整个文本就是一个字符串,简称为文本串,其中的页为文本串的子串,行又是页的子串。将它们按顺序方式存入计算机内存中,图中表回车符。,在输入程序的同时,文本编辑程序先为文本串建立相应的页表和行表,即建立各子串的存储映象。串值存放在文本工作区,而将页号和该页中的起始行号存放在页表中,行号、串值的存储起始地址和串的长度记录在行表,由于使用了行表和页表,因此新的一页或一行可存放在文本工作区的任何一个自由区中,页表中的页号和行表中的行
29、号是按递增的顺序排列的,如表4-3所示。设程序的行号从110开始。,下面我们就来讨论文本的编辑。 (1)插入一行时,首先在文本末尾的空闲工作区写入该行的串值,然后,在行表中建立该行的信息,插入后,必须保证行表中行号从小到大的顺序。若插入行145,则行表中从150开始的各行信息必须向下平移一行。 (2)删除一行时,则只要在行表中删除该行的行号,后面的行号向前平移。若删除的行是页的起始行,则还要修改相应页的起始行号(改为下一行)。 (3)修改文本时,在文本编辑程序中设立了页指针,行指针和字符指针,分别指示当前操作的页、行和字符。若在当前行内插入或删除若干字符,则要修改行表中当前行的长度。如果该行的
30、长度超出了分配给它的存储空间,则应为该行重新分配存储空间,同时还要修改该行的起始位置。 对页表的维护与行表类似,在此不再叙述,有兴趣的同学可设计其中的算法。,本章小结 本章主要介绍了如下一些基本概念: 串:串(或字符串)(String)是由零个或多个字符组成的有限序列。 主串和子串:一个串的任意个连续的字符组成的子序列称为该串的子串,包含该子串的串称为主串。 串的静态存储结构:类似于线性表的顺序存储结构,用一组地址连续的存储单元存储串值的字符序列的存储方式称为串的顺序存储结构。 堆存储结构:用一组空间足够大的、地址连续的存储单元存放串值字符序列,但其存储空间在程序执行过程中能动态分配的存储方式称为堆存储结构。 串的链式存储结构:类似于线性表的链式存储结构,采用链表方式存储串值字符序列的存储方式称为串的顺序存储结构。 串名的存储映象:串名的存储映象就是建立串名和串值之间的对应关系的一个符号表。 除上述基本概念以外,学生还应该了解串的基本运算(字符串拷贝(赋值、字符串的联接、求字符串的长度、子串的查询、字符串的比较)、串的静态存储结构的表示、串的链式存储结构的表示、串的堆存储结构的表示,能在各种存储结构方式中求字符串的长度、能在各种存储结构方式中利用C语言提供的串函数进行操作。,73,Thank you very much!,