Ukkonen的后缀树算法:'test and split'过程不清楚。

3

Ukkonen的在线构造算法

我在尝试理解“测试和分裂”过程时遇到了问题,该过程如下:

过程test-and-split(s, (k, p), t):
>1. if k ≤ p then
>2. let g'(s,(k',p'))=s' be the tk-transition from s
>3. if t=t(k'+p-k+1) then return (true,s)

我的问题是第二行到底是什么意思,如果从s开始并跟随t(k')而不是t(k),那么g'(s,(k',p'))如何仍然是一个tk转换?

1个回答

1
Probably you already figured it out and you don't need an answer anymore, but since I had the same problem in trying to understand it, and maybe it'll be useful for someone else in the future, the answer I think is the following one.
Ukkonen's on line construction algorithm 中的第7页可以看到:

...
STrie(T)中,两个明确的状态sr之间的转换路径所拼写出的字符串wSTree(T)中被表示为广义转移g′(s,w) = r。为了节省空间,字符串w实际上被表示为指向T的一对指针(左指针k和右指针p),使得tk . . . tp = w。通过这种方式,广义转移变成了g′(s, (k, p)) = r
这样的指针是存在的,因为必须有一个后缀Ti,使得TiSTrie(T)中的转换路径经过sr。我们可以选择最小的这样的i,并让kp指向从sr的转换路径所拼写出的此Ti的子字符串。如果tk = a,则转换g′(s, (k, p)) = r称为a–转换。每个s最多可以有一个a–转换,对于每个a ∈ Σ
...

这意味着我们正在寻找最小的索引kp,使得在Ttk...tp=w
=>如果T中有多个w的出现,我们总是使用kp来引用第一个。
现在,过程test–and–split(s,(k,p),t)测试具有规范参考对(s,(k,p))的状态是否为终点,即在STrie(Ti−1)中将具有ti–转换的状态。符号ti作为输入参数t给出。
算法的前几行如下:
   procedure test–and–split(s,(k,p),t):
1.   if k ≤ p then
2.     let g′(s,(k′,p′)) = s′ be the t(k)–transition from s;
3.     if t = t(k′+p−k+1) then return(true,s)
4.     else ...

第1行,我们检查状态是否为隐式状态(即当 k <= p 时)。

如果是这样,在第2行,我们希望找到从字符 T 的位置 k 开始的转移(也就是 tk)。注意,tk 必须等于 tk',但是索引 kk' 可以不同,因为我们总是指向 T 中字符串 w 的第一个出现位置(还要记住,从一个状态到另一个状态最多只能有一个以字符 tk 开头的转移,因此这是正确且唯一的方式)。

然后,在第3行,我们检查规范引用对(s,(k,p))所引用的状态是否为终点,即它是否具有ti -转换。状态(s,(k,p))是从状态s开始,沿着tk' -转换(即tk -转换,因为k' = k)可以到达的状态(隐式或非隐式),并且可以沿着(p - k)个字符进行。这就解释了tk′+p−k+1,其中+1是下一个字符,即我们正在检查它是否等于t(其中t = ti)。在这种情况下,我们到达了终点并返回true。

否则,从第4行开始,我们拆分转换g′(s,(k′,p′)) = s′以明确状态(s,(k,p))并返回新的显式状态。


1
从状态 s 开始,我们想要读取由指针对 (k,p) 表示的字符串 *w=t(k)...t(p)*。从 s 出发,只能有一个以字符 t(k) 开始的 t(k)-转移(即以字符 t(k) 开始的转移),但如果在 T 中存在另一个字符串 *w'=t(k')...t(p')*,使得 w'=wk'<k,则为了在转移函数 g' 中表示字符串 w,我们将使用拼写相同但具有较低指针的对 *(k',p')*,而不是对 (k,p)。在这种情况下,k'!=k,但总是 *t(k)=t(k')*。 - Tommy
1
实际上,我尝试使用我实现的Python算法(您可以在此处找到:https://github.com/tommy91/Algorithms/blob/master/suffixTree.py)构建“cacao”的后缀树,并且在函数test-and-split的3个调用中使用了k'!= k。正如您在我的代码中看到的那样,我插入了105-107行来通知使用k'!= k。 - Tommy
你的回答中,“查找最小索引k和p,使t_k. . . t_p = w in T”的部分似乎有些问题。 例如对于字符串"aabaac",最终的STree中,转换路径从r = (root, "a")而不是从根节点开始。该转换为g'(r, (2, 2)) = r',而不是g'(r, (1, 1))(在论文中采用了1-indexed表示法,但你的代码使用的是0-indexed)。 - pterodragon
在论文中写道:“必须存在一个后缀T_i,使得STrie(T)中T_i的转移路径经过s和r。选择最小的i。” 对于字符串“aabaac”,我上面提到的转移g'(r,(2,2))= r'源自后缀“aabaac”(i = 1),因为在STrie(T)中,“aabaac”的转移路径经过r和r'。并且对应于该转移路径的子字符串从第二个'a'开始,即“a” of a“a”baac。所以,k是2而不是1。如果我错了,请纠正我。 - pterodragon
结束这个答案之前,我刚刚发现了这个精彩的解释。如果你阅读它,在“第一个扩展:简单重复”的第4步中,你就会明白为什么k'可以不同于k,回答了原始问题。简单地说,为了构建树,我们一次向右移动一个字母。要使用下一个考虑的字符(比如A)扩展树,在当前节点(隐式或显式)中,我们寻找以该字符(A)开头的转换。但是,如果该转换存在,则它指的是该字符(A)的先前出现,因此k'<k。 - Tommy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接