您的位置:首页 > 编程语言 > Java开发

一篇关于AC自动机论文的译文:《Biosequence Algorithms, Spring 2005 Lecture 4: Set Matching and Aho-Corasick Algorit

2012-12-27 10:01 423 查看
之前初学AC自动机的时候看过一篇相当不错的论文   点这里当时看完就觉得对AC自动机有了相当好的认识。现在我再来回顾这篇文章的时候,觉得应该做点翻译,加深个人理解的同时可以帮助一些没心情看英文论文的朋友。可能有许多地方翻译不对,还望指点。

====================我是华丽的分割线===================

生物序列算法

第四讲:集合匹配及Aho-Corasicks算法

库奥皮奥大学计算机科学系

(Band译)

精确集合匹配问题中,我们需要在目标文本串T[1...m]中定位一个模式串集合P中每一个模式串P = {P1…PK}出现的位置。令n =  (公式贴不上来,自行对照PDF上的公式)。通过使用线性时间复杂度的算法(KMP之类的)k次来解决精确集合匹配问题的时间复杂度会是:

      O(|P1| + m + … + |Pk| + m) = O(n + km)

Aho-Corasick算法是一种解决精确集合匹配问题的经典算法。它可以在O(n + m + z)时间内解决该问题,这里z 是模式串在T中出现的次数。AC算法基于关键词树的一种改良结构。

关键词树:对于一个模式串集合P,它对应的关键词树是一棵这样的有根树:

1.树的每一条边都标有一个字符。

2.每个节点不同的两个树枝被不同的两个字符标记。

如此我们可以将关键词树上的每一个节点v都定义一个标记L(v):从根节点到v路径上所有边的标记连结成的字符串。

3.对于P上的每一个模式串Pi,关键词树上总有一个节点v使得L(v) = Pi

4.对于关键词树上每一个叶子v,总有一个Pi∈P使得L(v) = Pi

例子:图略了。。。。。

关键词树是查找字符串的高效字典方案。(一个关键词树对应一个字典)

建立

对模式串集合P = {P1…PK}的关键词树建立。

开始时关键词树只有一个根节点,将P中每一个模式串Pi一个接一个地插入到关键词树上,具体如下:

从根节点开始沿着被Pi的字符标记的树边移动;

l   如果树的路径在Pi走完之前就结束了(到达叶节点),那么在该叶节点后面为没跑完的Pi继续添加新的边和节点。

l  在路径结束的节点处储存Pi的标识符i

很明显,这些任务一共需要消耗O(|P1|  … + |Pk|) = O(n)的时间。

查找

在关键词树上查找一个模式串Px:从根节点开始,沿着被Pi字符标记的边的路径移动尽可能远。

l  如果路径最终停止在一个带有标识符的节点,那么Px在这个字典内。

l  如果路径最终在Px之前停止,那么字符串Px不在这个字典内。

这个过程只需要消耗O(|P|)的时间——这多高效啊!相比之下朴素的模式匹配算法需要O(nm)的时间。

接下来我们将扩展关键词树到自动机上,以支持线性时间复杂度的匹配性能。

AC自动机

状态:关键词树上的一个节点(每一个节点都对应一种状态)

初始状态:0 = 根节点(根节点被规定为状态0)

构造行为由下面三个函数决定:

 

1.goto函数g(q,a):函数的输出一个状态——从q当前状态进入并匹配目标字符a后的状态。

l  当边(q,v)被字符a标记,那么g(q, a) = v

l  对于每一个一个字符a,如果从根出发的边不存在一条被a标记,那么g(0,a)=0

→在初始状态下扫描到不匹配的字符,状态将维持在初始态。

l  否则g(q, a) = Ø(NULL)

 

2.fail函数f(q) :对于所有q≠0,此函数在匹配失败时将输出一个状态

l  f(q)输出一个节点状态——该节点f(q)对应的标记L[f(q)] = w是L(q)最长的后缀使得w是某一个模式串的前缀。

→ 一个fail状态转移总是不会错过任何一个潜在可能模式串的出现(a fail transition does not miss any potential occurrences)。

注;f(q)在关键词树上总是有定义,因为L(0)= Ø(空串)总是任意模式串的的前缀。

3.output函数out(q) :输出一个模式串的集合——状态进入q时匹配的模式串

AC自动机示例(图略)

q := 0;

for i:= 1 to m do

    while g(q, T[i]) = Ø do

        q := f(q);//按照fail

     q := g(q, T[i]);按照goto

     if out(q) ≠Ø then print [(i), out(q)];

end for

AC搜索的复杂度

定理:在一个AC自动机上搜索一个字符串T[1...m]需要时间O(m + z),这里z是模式串的出现次数。

证明 对于每一个目标字符,自动机进行0次或多次fail转移操作,紧接着进行一次goto。

每一次goto要不停留在根节点,要不使得状态的深度加1 → 状态q的深度增加次数 <= m。每一次fail都会使得q更加靠近根节点 → fail转移的总次数 <= m

z次的模式串出现可以在z * O(1) = O(z)时间内输出(输出模式串标识符及出现位置)

AC自动机的建立

一个AC的建立可以分为下面两个阶段:

阶段一:

1. 为模式串集合P建立关键词树

l  当每一个Pi∈P添加到关键词树时,被Pi标记的节点v,设置其out(v) := {Pi}。

2.完成根节点的goto函数,对于每一个a∈∑没标记从根出发的边,设定g(0,a) := 0(∑是一个固定的字母表)。

阶段一需要O(n)是时间。(图略。。。。。。)

 

阶段二:

Q := emptyQueue();

for a∈∑ do

   if q(0,a) = q≠0 then

      f(q) := 0 enqueue(q,Q);

end for;

while not isEmpty(Q) do

r:= dequeue(Q);

for a∈∑ do

    if g(r,a) = u ≠Ø then

         enqueue(u, Q); v := f(r);

         while g(v,a) = Ø do v:= f(v);//(*)

         f(u) := g(v,a);

         out(u) := out(u) ∪ out(f(u))

       end for

   end while

 

   这有什么用?

阶段二的解释

fail函数及output函数都是按照节点的广度优先次序来运算的。

→ 越靠近根的节点其函数最先被计算。

 

考虑节点r和u = g(r,a),明显r是u的父节点,且有L(u) = L(r)a。

现在f(u)应该怎么确定呢?

 

答:f(u)应该是被L(u)后缀标记的,且深度最深的节点。

在上面伪代码(*)行,是通过找到最深的节点v使得L(v)是L(r)的后缀,这样g(v ,a)(=f(u ))就能被定义。(注意v和g(v,a)都有可能是根节点)。

 

另外   out(u) := out(u) ∪ out(f(u))?

这是因为被f(u)认为匹配的模式串都是L(u)的合法后缀。所以也应该被u认为是匹配的模式串(因为L(f(u))是L(u)的后缀,根据后缀关系的传递性,L(f(u))的后缀也是L(u)的后缀,译者注)。

AC自动机建立过程的复杂度

阶段二也能在O(n)时间内运行。

广度优先遍历需要的时间和树的大小成正比,所以也是O(n),好的。。

在算法(*)行的f转移时是否也拥有O(n)的上界呢?答案是肯定的。

AC自动机:失败转移的数量。

考虑点把模式串a1…al加入树时路径上的节点u1……ul和他们f函数的节点深度,定义为 df(u1)….df(ul)(全部大于0)

df(ui+1) <= df(ui) + 1 → 在路径上df最多只增加l次。当我们定位f(ui+1)时,(*)的操作总是将v向根节点靠近,使得df(ui+1)总是比df(ui)至少小1.

→(*)行运行次数小于l次(l是模式串的长度)→对于所有字符串,(*) 行运行时间总是小于n次。

AC自动机:output函数的归并

运行

out(u) := out(u) ∪ out(f(u))

会很耗时吗?

不会:在上述操作执行前,out(u) = Ø 或者out(u) = {L(u)}总是成立。在out(f(u))函数中的每一个模式串总是比L(u)短。因此集合不相交 → output集合可以被设置为链表,这样集合归并可以在常数时间内完成。

生物学的应用:

1.已知模式库匹配

(这里略去。。。)

2.通配符匹配

令 ∅ 是一个能匹配任意一个字符的通配符

例如,ab∅∅c∅在下面字符串的2号和7号位置出现

    1234567890123

    xabvccababcax

(转录因子等略去。。。)

如果通配符的数量的上界是一个常数,带通配符的模式串匹配可以通过统计无通配符子串在目标串内出现的位置,在线性复杂度时间内完成。

令P = {P1…PK} 是模式串P’以通配符为分界分解出来的子串。令l1,…lk是这些子串在P’内的结束位置。

预处理:为模式串集合P建立AC自动机;

初始化出现量统计:for i = 1 to |T| do C[i] = 0;

对目标串T利用AC自动机进行搜索

当模式串Pj在T的位置i >= lj被找到,就让C[i-lj+1]自增1。

每一个i满足C[i] = k都是P’在T的出现的起点位置。

(例子略。。。)

通配符模式匹配复杂度

令|P| = n 且|T| = m

预处理:O(n+m)( ← )(公式贴不上来,自行对照PDF上的公式)

搜索:O(m+z),这里z是模式子串出现的次数。

每一次子串的出现都会使得C中一个单元自增1,这些单元一共自增最多k次 → z <= km (= O(m)  (这里假设k以一个常数为上界。)

我们可以得出以下结果:

定理:如果一个模式串的通配符数量以一个常数为上界,那么带通配符的精确匹配问题可以在O(|P| + |T|)时间内完成。

======================================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm AC自动机论文