字符串中的快速模式匹配2

来源:互联网 发布:mac ae渲染快捷键 编辑:程序博客网 时间:2024/06/05 03:45

字符串中的快速模式匹配2

2、算法程序

          模式匹配过程的一般形式:
将模式置于最左边;
while 模式没有完全匹配and文本没有读完do
begin
while模式字符不同于当前文本字符do
正确移动模式;
移动置文本下一个字符;

end;

       方便起见,我们假定输入文本以text[1:n]的一维数组描述,并且模式以pattern[1:m]的一维数组描述。并且m>0以保证模式非空。设k、j为正整数,text[k]表示当前文本字符,pattern[j]表示对应的模式字符;因此,模式应该与文本的p+1至p+m位置的字符对应,其中k=p+j。那么上面的程序可以替换为下面的格式:

j:=k:=1;

while j<=m and k<=n do

begin

while j>0 and text[k]!=pattern[j]

do j:=next[j];

k:=k+1;

j:=j+1;

end;

        如果程序最后的结果是j>m说明找到了匹配,文本中的位置就是k-m至k-1;但是如果j<=m那么说明文本已经遍历完了。(这里的and是条件与并不是用来判断text[k]!=pattern[j]除非j>0这个关系)这个程序有个奇怪的特点,换句话就是内循环的j:=next[j]操作没有外循环的k:=k+1操作频繁;实际上,内循环执行的是非常少的,因为通常模式右移的比文本指针移动的要慢很多。

        为了严格地证明上面那个程序是正确的,我们可以用下面的这个定理:让p=k-j(在我们的假设条件中,文本的位置在模式第一个字符的前面),那么对于1<=i<j我们有text[p+i]=pattern[i];但是对于0<=t<p、1<=i<=m我们却有text[t+i] !=pattern[i](在p的左边不会有模式的完全匹配)。

        只要我们能计算出表next那么当我们执行j:=next[j]操作时上面的那些关系就不会变,那么程序也就是正确的了。现在,就让我们来计算一下next表。当程序执行到j:=next[j]时,我们知道j>0并且前j个字符取决于并且包括text[k]是pattern[1]...pattern[j-1]x 并且x!=pattern[j]。我们需要找到的是使这些字符可以成功匹配模式所需移动模式的最少步数;换句话说,我们想要next[j]等于小于j的最大数i,使得前i个输入字符pattern[1]...pattern[i-1]x并且pattern[i]!=pattern[j]。(如果没有这样的i存在,就让next[j]=0)有了next[j]的定义之后,便很简单就证明了对于k-j<=t<k-next[j]有text[t+1]...text[k]!=pattern[1]...pattern[k-1];因此所述的关系确实是不变的,我们的程序也是正确的。

        现在我们就要着手解决之前搁置的任务了——计算next[j]表。在定义next[j]时如果我们不考虑pattern[i]!=pattern[j]就会简单很多,所以我们先从不考虑这个条件。设f[j]为满足pattern[1]...pattern[i-1]=pattern[j-i+1]...pattern[j-1]小于j的i可取的最大值;因为这个条件对于i=1不成立,所以当j>1时要有f[j]>=1。方便起见,我们让f[1]=0。那么第一部分中例题所用的模式对应的f表为:


       如果pattern[j]=pattern[f[j]]那么f[j+1] = f[j]+1否则,我们可以使用跟上面计算text=pattern基本相同的模式匹配算法计算f[j+1]。(注意f[j]问题与匹配算法的不变条件相似性,我们的程序计算的是小于等于k的最大j使得pattern[1]...pattern[j-1]=text[k-j+1]...text[j-1],所以我们可以改变一下之前的程序来解决这个问题。)假设f[j]和next[1]...next[j-1]已经计算出来了,下面的程序将会计算f[j+1]:

t:=f[j];

while t>0and pattern[j]!=pattern[t] do 

t:=next[t];

f[j+1]:=t+1;

      程序的正确性跟之前的证明一样;我们可以想象成两个模式的拷贝,其中一个相对于另一个向右面滑动。例如:假设我们在上面的例子中计算出f[8]=5,下面我们考虑一下f[9]的计算。这个过程是:


       因为pattern[8]!=b,我们右移上面的模式拷贝,这样就知道了下方的拷贝最近扫描的最多字符为abcax(x!=b)。查找next表可以知道要右移四位,就得到:


       又一次匹配失败。接下来的移动让t=0,所以f[9] = 1。

      一旦我们知道了如何求得f,那么计算next[j]就很容易了。对于j>1,定义下面的一个比较:


       因此不需要在内存中存储f[j]的值,我们就可以按照下面的程序计算表next了:

j:=1;t:=0;next[1]:=0;

while j<mdo

begin comment t = f[j];

while t>0and pattern[j]!=pattern[t] do

t:=next[t];

t:=t+1;j:=j+1;

if pattern[j]=pattern[t]

then next[j]:=next[t];

else next[j]:=t;

end.

       这个程序花费O(m)时间单元,跟模式匹配花费O(n)时间的原因一样:内循环t:=next[t]总是右移上面的模式,因此他最多被执行m次。
0 0
原创粉丝点击