串的匹配文档格式.docx
- 文档编号:19956038
- 上传时间:2023-01-12
- 格式:DOCX
- 页数:7
- 大小:38.38KB
串的匹配文档格式.docx
《串的匹配文档格式.docx》由会员分享,可在线阅读,更多相关《串的匹配文档格式.docx(7页珍藏版)》请在冰豆网上搜索。
j=1;
}
//指针后退重新开始匹配
}
if(i>
T[0])returni-T[0];
elsereturn0;
}//Index
算法
1
在算法1的函数过程中,分别利用计数指针i和j指示主串S和模式串T中当前正待比较
的字符位置。
算法的基本思想是:
从主串S的第pos个字符起和模式的第一个字符比较之,
若相等,则继续逐个比较后续字符,否则从主串的下一个字符起再重新和模式的字符比较之。
依次类推,直至模式T中的每个字符依次和主串S中的一个连续的字符序列相等,则称匹配成
功,函数值为和模式T中第一个字符相等的字符在主串S中的序号,否则称匹配不成功,函数值
为零。
图1展示了模式T="
abcac"
和主串S的匹配过程(pos=1)。
算法1的匹配过程易於理解,且在某些应用场合,如文本编辑等,效率也较高,例如,在检查模式
"
STING"
是否存在於下列主串中时,
ASTRINGSEARCHINGEXAMPLECONSISTINGOFSIMPLETEXT"
上述算法中的WHILE循环次数(即进行单个字符比较的次数)为41,恰好为
(Index+T[0]-1)+4这就是说,除了主串中呈黑体的四个字符,每个字符比较了两次以外,其
它字符均只和模式进行一次比较。
在这种情况下,此算法的时间复杂度为O(n+m)。
其中n
和m分别为主串和模式的长度。
然而,在有些情况下,该算法的效率却很低。
例如,当模
式串为"
00000001"
,而主串为
00000000000000000000000000000000000000000000000000001"
时,由于模式中前7个字符
均为"
0"
,又,主串中前52个字符均为"
,每趟比较都在模式的最后一个字符出现不等,
此时需将指针i回溯到i-6的位置上,并从模式的第一个字符开始重新比较,整个匹配过程
中指针i需回溯45次,则WHILE循环次数为46*9(index*m)。
可见,算法1在最坏情况下
的时间复杂度为O(n*m)。
这种情况在只有0、1两种字符的文本串处理中经常出现,因为在
主串中可能存在多个和模式串“部分匹配”的子串,因而引起指针i的多次回溯。
01串可以
用在许多应用之中。
比如,一些计算机的图形显示就是把画面表示为一个01串,一页书就
是一个几百万个0和1组成的串。
在二进位计算机上实际处理的都是01串。
一个字符的ASCII
码也可以看成是八个二进位的01串。
包括汉字存储在计算机中处理
时也是作为一个01串和其它的字符串一样看待。
因此在下一节,我们将介绍另一种较好的
模式匹配算法。
2
模式匹配的一种改进算法
这种改进算法是D.E.Knuth与V.R.Pratt和J.H.Morris同时发现的,因此人们称它
为克努特-莫里斯-普拉特操作(简称为KMP算法)。
此算法可以在O(n+m)的时间数量级上完
成串的模式匹配操作。
其改进在於:
每当一趟匹配过程中出现字符比较不等时,不需回溯i
指针,而是利续进行比较。
下面先从具体例子看起。
回顾图1中的匹配过程示例,在第三趟的匹配中,当i=7,j=5字符比较不等时,又从i=4,
j=1重新开始比较。
然后,经仔细观察可发现,在i=4和j=1;
i=5和j=1以及i=6和j=1这三
次比较都是不必进行的。
因为从第三趟部分匹配的结果就可得出,主串中第4、5和6个字
符必然是‘b’、‘c’和‘a’(即模式串中第2、3和4个字符)。
因为模式中的第一个字符是
a,因此它无需再和这三个字符进行比较,而仅需将模式向右滑动三个字符的位置继续进行
i=7、j=2时的字符比较即可。
同理,在第一趟匹配中出现字符不等时,仅需将模式向右移动
二个字符的位置继续进行i=3、j=1时的字符比较。
由此,在整个匹配的过程中,i指针没有
回溯。
现在讨论一般情况。
假设主串为s[1..n],模式串为p[1..m],从上例的分析可知,为了实
现改进算法,需要解决下述问题:
当匹配过程中产生“失配”(即s[i]!
=p[j])时,模式串“向
右滑动’’可行的距离多远,换句话说,当主串中第i个字符与模式中第j个字符“失配’’
(即比较不等)时,主串中第i字符(i指针不回溯)应与模式中哪个字符再比较?
假设此时应与模式中第k(k<
j)个字符继续比较,则模式中前k-1个字符的子串必须满足下列
关系式,且不可能存在kk>
k满足下列关系式而已经得到的“部分匹配”的结果推得下列等
式
p[1..k-1]==s[i-k+1..i-1]
而已经得到的部分结果是
p[j-k+1..j-1]==s[i-k+1..i-1]
推得以下结果
p[1..k-1]==p[j-k+1..j-1]
反之,若模式串中存在满足式的两个子串,则当匹配过程中,主串中第i个字符与模式中第
j个字符比较不等时,仅需将模式向右滑动至模式中第是个字符和主串中第i个字符对齐,
此时,模式中头是k-1个字符的子串p[1..k-1]必定与主串中第i个字符之前长度为k-1的子
串s[i-k+1..i-1]相等,由此,匹配仅需从模式中第i个字符与主串中第j个字符比较起继续进
行。
若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应字符“失配”时,在模
式中需重新和主串中该字符进行比较的字符的位置。
由此可引出模式串的next函数的定义:
next[j]=0
当j=1时
next[j]=Max{k|1<
k<
j&
p[1..k-1]==p[j-k+1..j-1]当此集合不空时
next[j]=1其它情况
由此定义可推出下列模式串的next函数值:
在求得模式的next函数之后,匹配可如下进行:
假设以指针i和j分别指示主串和模式
中正待比较的字符,令i的初值为pos,j的初值为1。
若在匹配过程中s[i]==p[j],则i和j
分别增1,否则,i不变,而j退到next[j]的位置再比较,若相等,则指针各自增1,否则j
再退到下一个next值的位置,依次类推,直至下列两种可能:
一种是j退到某个next值
(next[next...next[j]...])时字符比较相等,则指针各自增1继续进行匹配;
另一种是j退到值
为零(即模式的第一个字符“失配”),则此时需将模式继续向右滑动一个位置,即从主串的
下一个字符s[i+1],起和模式重新开始匹配。
下图所示正是上述匹配过程的一个例子。
KMP算法如算法2所示,它在形式上和算法1极为相似。
不同之处仅在於:
当匹配过
程中产生“失配”时,指针i不变,指针j退回到next[j]所指示的位置上重新进行比较,并
且当指针j退至零时,指针i和指针j需同时增1。
即若主串的第i个字符和模式的第1个字
符不等,应从主串的第i+1个字符起重新进行匹配。
intIndex_KMP(SStringS,SStringT,intpos){
//利用模式串T的next函数求T在主串s中第pos个字符之后的位置的
//KMP算法。
其中,T非空,1≤pos≤StrLength(S)。
j=1;
=T[0]){
if(j=0||S[i]==T[j]){++i;
++j;
//继续比较后继字符
elsej=next[j];
//模式串向右移动
//匹配成功
.
}//Index_KMP
算法2
KMP算法是在已知模式串的next函数值的基础上执行的,那么,如何求得模式串的
next函数值呢?
从上述讨论可见,此函数值仅取决于模式串本身而和相匹配的主串无关。
分析其定义出发用递推的方法求得next函数值。
由定义得知
next[1]=0
设next[j]=k,这表明在模式串中存在下列关系:
p[1..k-1]==p[j-k+1..j-1]
其中k为满足1<
j的某个值,并且不可能存在kk>
k满足等式。
此时next[j+1]=?
可能有两
种情况:
(1)若p[k]==p[j],则表明在模式串中
p[1..k]==p[j-k+1..j]
并且不可能存在kk>
k满足等式,这就是说next[j+1]==k+1,即
next[j+1]==next[j]+1
(2)若p[k]!
=p[j],则表明在模式串中
p[1..k]!
=p[j-k+1..j]此时可把求next函数值的问题看成是一个模式匹配的
问题,整个模式串既是主串又是模
式串,而当前在匹配的过程中,已有p[j-k+1]==p[1],p[j-k+1]=p[2],…,p[j-1]=p[k-1],则当
p[j]!
=p[k]时应将模式向右滑动至以模式中的第next[k]个字符和主串中的第j个字符相比较。
若next[k]==kk,且p[j]==p[kk],则说明在主串中第j+1个字符之前存在一个长度为kk,(即
next[k])的最长子串,和模式串中从首字符起长度为kk的子串相等,即
p[1..kk]==p[j-kk+1..j]
(1<
kk<
j)
这就是说
next[j+1]=kk+1即
next[j+1]==next[k]+1
同理,若p[j]!
=p[kk],则将模式继续向右滑动直至将模式中第next[kk]个字符和p[j]对
齐,……,依次类推,直至p[j]和模式中某个字符匹配成功或者不存在任何kk(1<
j)满足
等式,则
next[j+1]=1
例如:
图4中的模式串,已求得前6个字符的next函数值,现求next[7],因为next[6]=3,
又p[6]!
=p[3],则需比较p[6]和p[1](因为next[3]=1),这相当于将子串模式向右滑动。
由于P[6]!
=p[1],而且next[1]=0,所以next[7]=1,而因为p[7]=p[1],则next[8]=2。
根据上述分析所得结果,仿照KMP算法,可得到求next函数值的算法,如算法3所示。
voidget_next(SStringT,int&
next[]){
//求模式串T的next函数值并存入数组next。
i=1;
next[l]=O;
j=0;
while(i<
T[O]){
if(j==0||T[i]==T[j]){++i;
++j;
next[i]=j;
}
else
j=next[j];
}//get_next
算法3
算法3的时间复杂度为O(m)。
通常,模式串的长度m比主串的长度n要小得多,因此,
对整个匹配算法来说,所增加的这点时间是值得的。
最后,要说明两点:
1)虽然算法1的时间复杂度是O(n*m),但在一般情况下,其实际的执行时间近似于
O(n+m),因此至今仍被采用。
KMP算法仅当模式与主串之间存在许多“部分匹配”的情况
下才显得比算法1快得多。
但是KMP算法的最大特点是指示主串的指针不需回溯,整个匹
配过程中,对主串仅需从头至尾扫描一遍,这对处理从外设输入的庞大文件很有效,可以边
读入边匹配,而无需回头重读。
2)前面定义的next函数在某些情况下尚有缺陷。
例如模式"
aaaab"
在和主串"
aaabaaaab"
匹配时,当i=4,j=4时s.ch[4]!
=t.ch[4],由next[j]的指示还需进行i=4、j=3,i=4、j=2,i=4、j=1
等三次比较。
实际上,因为模式中第1、2、3个字符和第4个字符都相等,因此不需要再和
主串中第4个字符相比较,而可以将模式一气向右滑动4个字符的位置直接进行i=5,j=1
时的字符比较。
这就是说,若按上述定义得到next[j]=k,而模式中p[j]==p[k],则当主串中
字符s[i]和p[j]比较不等时,不需要再和p[k]进行比较,而直和P[next[k]]进行比较,换句话
说,此时的next[j]应和next[k]相同。
由此可得计算next函数修正值的算法如算法4所示。
此时匹配算法不变。
voidget_nextval(SStringT,int&
nextval[]){
//求模式串T的next函数修正值并存入数组nextval。
nextval[1]=0;
i=0;
T[0]){
if(j=0||T[i]==T[j]){
++i;
++j;
if(T[i])!
=T[j])
nextval[i]=j;
elsenextval[I]=nextval[j];
j=nextval[j];
(End)
第1页
第2页
第3页
第4页
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 匹配