再谈AC算法

在我的《AC算法详解》一文中,还存在一个很大的疏漏,那就是用蛮力法计算自动机的fail跳转表,这实际上是一个极度低效的方法。在Aho,Corasick两人的论文中,给出的是逐层求fail表的方法,两人证明了这个方法可以在O(n)(其中n为所有模式串的总长度和)时间复杂度内计算出模式集合P的fail跳转表,下面,我就来补完这一算法的执行步骤。还是以之前的模式集合P{he,she,his,hers,era}为例来说明,其构建的goto表如下图所示。这里为了描述方便,我们将该图简记为g,令g(a,b)为图中的一个转换a代表当前状态,b代表当前状态下的一个合法输入。例如g(1,e)=2表示节点1在输入字符e的情况下转换到节点2。

再谈AC算法_第1张图片

我们利用如下方法来求出g中所有节点的fail值。首先,令第1层节点的fail值都为0。然后,将第1层的节点依次加入队列queue尾部。然后,从队列queue的头部依次取出节点。对于取出的节点m,及其与子节点的状态转移g(m,i)=n,fail[n]的计算方法为,我们令fail[m]=p,如果p!=0且g(p,i)=0,我们令p=fail[p],直到p=0,或者g(p,i)!=0为止,此时fail[n]=g(p,i)。对于状态n,如果output[p]包含匹配的模式串,则将output[p]中的匹配的模式,全部加入output[n]中。如果节点n仍有子节点,则将节点n加入队列queue的尾部,直到队列queue中的所有节点处理完毕为止,我们就得到了除状态0之外的所有节点的fail值。下面举上图的三个例子说明。

1. fail[2]=g(fail[1],e)=g(0,e)=10,fail[6]=g(fail[1],i)=g(0,i)=0,这里对于第1层节点1,3,10,我们有fail[1]=0,fail[3]=0,fail[10]=0。

2. 对于fail[5],有g(4,e)=5,g(fail[4],e)=g(1,e)=2!=0,所以fail[5]=2,因为output[2]包含模式串he,所以我们要将output[2]中的内容添加到output[5]中,此时output[5]={she,he}。

3. 对于fail[9],我们有g(8,s)=9,g(fail[8],s)=g(11,s)=0,但是此时p=fail[8]!=0,所以我们接着考察fail[p]=fail[fail[8]]=fail[11]=0,此时fail[11]=0,g(fail[11],s)=g(0,s)=3,所以fail[9]的最终结果是3。由于节点3的output值为空,无需新增output[9]的内容。

你可能感兴趣的:(算法,output)