Double-Array Trie快速入门
shiqi.cui<cuberub@gmail.com>
May 24, 2009
1. Trie
Trie是一种搜索树,因“Retrieval”而得名。在以Trie树组织的词典里,所有词条的公共前缀是压缩存储的,即只会存储一份,所以又称前缀树。如图所示:
Trie可以理解为确定有限状态自动机,即DFA。在Trie树中,每个节点表示一个状态,每条边表示一个字符,从根节点到叶子节点经过的边即表示一个词条。查找一个词条最多耗费的时间只受词条长度影响,因此Trie的查找性能是很高的,跟哈希算法的性能相当。
2. Trie存储方式
Trie可以按照树的方式存储。每个节点包含n个指针,分别指向n个后续节点,每条边对应着一个输入字符。这样,每个节点的指针个数是跟字符表的大小相关的。如果按照链表的方式组织n个指针,查询的效率会比较低;如果以定长数组表示n个指针,占用的空间会比较大,基本是不可接受的。
Trie也可以按照DFA的方式存储,即表示为转移矩阵。行表示状态,列表示输入字符,(行, 列)位置表示转移状态。这种方式的查询效率很高,但由于稀疏的现象严重,空间利用效率很低。也可以采用压缩的存储方式即链表来表示状态转移,但查询效率无法满足要求。
为了解决上面的问题,有学者依次设计出了Four-Array Trie,Triple-Array Trie和Double-Array Trie结构,其得名源于内部采用的数组的个数。
3. Double-Array Trie
Double-Array Trie包含base和check两个数组。base数组的每个元素表示一个Trie节点,即一个状态;check数组表示某个状态的前驱状态。
base和check的关系满足下述条件:
base[s] + c = t
check[t] = s
其中,s是当前状态的下标,t是转移状态的下标,c是输入字符的数值。如图所示:
4. 查询过程
根据上述公式,查找某个字符串就非常简单。
假设初始状态为t0,字符序列是(c1, c2, …, cn)。那么,输入c1后的状态为t1 = base[t0] + c1,以此类推。
如果到某个状态是不合法的,那么查询失败;如果转到状态tn = base[tn-1] + c,并且tn是结束状态,那么查询成功;如果tn不是结束状态,那么查询失败。
5. 构建过程
首先,初始化base和check数组,元素默认值是0。随机确定初始状态t0及其base值,如t0=0, base[t0]=1。
对于插入词条,计算输入每个字符后的base位置。
如果该位置为空,则表示该位置可以插入,然后转到下一个字符;
如果该位置已有值,表示该位已经被其他的状态占用,这样需要调整其前驱状态的base值,以保证状态不会冲突,这个过程称为relocate。