前面blog讲了后缀树SuffixTree相关的介绍,还有粗暴的平方时间构造法,今天我们来看下后缀树在Ukkonen优化下的线性时间构造方式。
本篇博客原文出自地址:http://www.cnblogs.com/gaochundong/p/suffix_tree.html 在此基础上做了小幅度的改动,特此说明。
Suffix Tree 与 Trie 的不同在于,边(Edge)不再只代表单个字符,而是通过一对整数 [from, to] 来表示。其中 from 和 to 所指向的是 Text 中的位置,这样每个边可以表示任意的长度,而且仅需两个指针,耗费 O(1) 的空间。
首先,我们从一个最简单的字符串 Text = "abc" 开始实践构建后缀树,"abc" 中没有重复字符,使得构建过程更简单些。构建过程的步骤是:从左到右,对逐个字符进行操作。
abc
第 1 个字符是 "a",创建一条边从根节点(root)到叶节点,以 [0, #] 作为标签代表其在 Text 中的位置从 0 开始。使用 "#" 表示末尾,可以认为 "#" 在 "a" 的右侧,位置从 0 开始,则当前位置 "#" 在 1 位。
其代表的后缀意义如下。
第 1 个字符 "a" 处理完毕,开始处理第 2 个字符 "b"。涉及的操作包括:
- 扩展已经存在的边 "a" 至 "ab";
- 插入一条新边以表示 "b";
其代表的后缀意义如下。
这里,我们观察到了两点:
- "ab" 边的表示 [0, #] 与之前是相同的,当 "#" 位置由 1 挪至 2 时,[0, #] 所代表的意义自动地发生了改变。
- 每条边的空间复杂度为 O(1),即只消耗两个指针,而与边所代表的字符数量无关;
接着再处理第 3 个字符 "c",重复同样的操作,"#" 位置向后挪至第 3 位:
其代表的后缀意义如下。
此时,我们观察到:
- 经过上面的步骤后,我们得到了一棵正确的后缀树;
- 操作步骤的数量与 Text 中的字符的数量一样多;
- 每个步骤的工作量是 O(1),因为已存在的边都是依据 "#" 的挪动而自动更改的,仅需为最后一个字符添加一条新边,所以时间复杂度为 O(1)。则,对于一个长度为 n 的 Text,共需要 O(n) 的时间构建后缀树。
当然,我们进展的这么顺利,完全是因为所操作的字符串 Text = "abc" 太简单,没有任何重复的字符。那么现在我们来处理一个更复杂一些的字符串 Tex