Trie(Retrieval Tree)又称前缀树,可以用来保存多个字符串,并且非常便于查找。在trie中查找一个字符串的时间只取决于组成该串的字符数,与树的节点数无关。因此,它的查找速度通常比二叉搜索树更快。trie的结构很简单,每条边表示一个字符,从根节点到叶节点就可以表示一个完整的字符串。所以,如果用trie表示一组英文单词,就是一颗26叉数;表示一组自然数,就是一颗10叉树。直观上,实现trie很简单,比如实现英文单词的trie,使用如下的节点构造树:

:::c
struct node
{
char chr;
struct node *edges[26];
};

这样做虽然简单,但没有很好的利用内存,edges数组肯定很多都是闲置的,如果使用到更多字符的话,这种浪费会更严重。这里介绍一种基于数组结构的trie实现方式,不仅节省内存,而且查询速度更快。基于数组查表的时间复杂度为O(|P|),基于平衡树的时间复杂度为O(|P|log|Σ|),其中,P表示查询的字符串长度,Σ表示字符集合。

基于数组的实现方式,把trie看作一个DFA,树的每个节点对应一个DFA状态,每条从父节点指向子节点的有向边对应一个DFA变换。遍历从根节点开始,字符串的每个字符作为输入用来确定下一个状态,直到叶节点。

三数组trie

trie可以用三个数组来表示:

  • base: 其中的每个元素对应trie上的一个节点,即DFA的状态。对于节点s,base[s]nextcheck在状态转换表中的起始位置。如果base[i]为负值或没有next转换,表示该状态为一个词语。
  • next: 和check搭配使用,提供数据池分配稀疏向量,用于保存trie状态转换表的各行数据。来自各个节点的转换向量保存在此数组中。
  • check: 与next平行使用,它与next相同位置的元素记录了next中对应元素的拥有者,即之前的状态。

所谓trie*状态转换表,即状态转换矩阵,是DFA里的概念:横行是状态转换向量*,比如,状态s接受n种输入字符c1,...,cn,即构成状态s的状态转换向量;纵列是各种状态,即trie的各节点。

对于输入字符c,从状态s转换到t,用三数组trie可以表示为:

check[base[s]+c] = s
next[base[s]+c] = t

类似下图:

遍历树

对于给定状态s和输入字符c的遍历算法表示如下:

t := base[s]+c
if check[t] = s then
next state := next[t]
else
fail
endif

创建树

当插入一个状态转换,比如,输入字符c,状态从s转换到t,此时,数组元素next[base[s]+c]]应该是空的,否则,整个占用该数组元素位置的状态转换向量或者状态s的状态转换向量必须要重新迁移(relocate)。实际过程中选择代价较小的那个。假设迁移状态s的状态转换向量,重新分配的起始位置为b,整个过程很简单:

Relocate(s: 状态, b: next数组中新的起始位置)
begin
foreach 状态s后的每种输入字符c
begin
check[b+c] := s 标记前件状态
next[b+c] := next[base[s]+c] 复制原先的状态数据
check[base[s]+c] := none 释放原先的状态数据
end
base[s] := b 完成迁移
end

新位置b的选择比较关键,应该避免迁移过程中再次发生冲突。整个过程如下图,实线表示迁移前,虚线表示迁移后:

双数组trie

三数组trie的nextcheck数组元素之间存在间隙,可以将basenext合并,把base数组中的表示穿插在next中进行,而next中有值的项直接表示为base的内容,这样就得到两个平行的数组basecheck,即双数组trie。

对于输入字符c,从状态s转换到t,用双数组trie可以表示为:

check[base[s]+c] = s
base[s]+c =t

类似下图

遍历

对于给定状态s和输入字符c的遍历算法表示如下:

t := base[s] + c;
if check[t] = s then
next state := t
else
fail
endif

创建树

双数组trie的创建类似三数组trie,但重新迁移方法略有不同:

Relocate(s: 状态, s: base数组中的起始位置)
begin
foreach 状态s后的每种输入字符c
begin
check[b+c] := s 标记前件状态
base[b+c] := base[base[s}+c] 复制原先的状态数据
foreach 状态base[s]+c后的每种输入字符d
begin
check[base[base[s]+c]+d] := b+c
end
check[base[s]+c] := none 释放原先的状态数据
end
base[s] := b 完成迁移
end

整个过程如下图:

参考

http://blog.jqian.net/post/trie.html

Trie树的数组实现原理的更多相关文章

  1. Wannafly挑战赛10F-小H和遗迹【Trie,树状数组】

    正题 题目链接:https://ac.nowcoder.com/acm/contest/72/F 题目大意 \(n\)个字符串,包括小写字母和\(\#\).其中\(\#\)可以替换为任意字符串.求有多 ...

  2. 字典树(Trie树)的实现及应用

    >>字典树的概念 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树.与二叉查找树不同,Trie树的 ...

  3. Day2:T4求逆序对(树状数组+归并排序)

    T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*( ...

  4. bzoj4785 [Zjoi2017]树状数组

    Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进 ...

  5. ST表与树状数组

    ST表  st表可以解决区间最值的问题.可以做到O(nlogn)预处理 ,O(1)查询,但是不支持修改. st表的大概思路就是用st[i][j]来表示从i开始的2的j次方个树中的最值,查询时就从左端点 ...

  6. 【BZOJ】3173: [Tjoi2013]最长上升子序列(树状数组)

    [题意]给定ai,将1~n从小到大插入到第ai个数字之后,求每次插入后的LIS长度. [算法]树状数组||平衡树 [题解] 这是树状数组的一个用法:O(n log n)寻找前缀和为k的最小位置.(当数 ...

  7. HDU_2642_二维树状数组

    Stars Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/65536 K (Java/Others)Total Submi ...

  8. 洛谷 P4396 (离散化+莫队+树状数组)

    ### 洛谷P4396  题目链接 ### 题目大意: 有 n 个整数组成的数组,m 次询问,每次询问中有四个参数 l ,r,a,b .问你在[l,r] 的区间内的所有数中,值属于[a,b] 的数的个 ...

  9. 树状数组的理解(前缀和 and 差分)

    二更—— 有神仙反映数星星那个题外链炸了,我决定把图给你们粘一下,汉语翻译的话在一本通提高篇的树状数组那一章里有,同时也修改了一些汉语语法的错误 这段时间学了线段树组,当神仙们都在学kmp和hash的 ...

随机推荐

  1. ie6 PNG图片透明

    _background:none; _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/videoTips.pn ...

  2. PAT 1067 试密码(20)(代码)

    1067 试密码(20 分) 当你试图登录某个系统却忘了密码时,系统一般只会允许你尝试有限多次,当超出允许次数时,账号就会被锁死.本题就请你实现这个小功能. 输入格式: 输入在第一行给出一个密码(长度 ...

  3. Android.HowToDesignPluginArchitectureInAndroidApp

    There is a tools called "dx", this tool can transfer Java Binary Code into Android Dalvik ...

  4. What is MaxiSys Pro MS908P Software Advantage

    The Autel Maxisys Pro Diagnostic Platform is an evolutionary smart solution for specialized automoti ...

  5. delphi sdk 函数个数知多少?

    pascal用了这么久 那么您知道他有多少个函数,过程? 笔者统计了一下, delphi 7    21579个delphi xe2   41145个lazarus 1.12  70987个 ==== ...

  6. [SoapUI] Property Expansion in soapUI

    1. Property Expansion in soapUI SoapUI provides a common syntax to dynamically insert ("expand& ...

  7. Mockplus3.5.0.1新增标注功能

    Mockplus3.5.0.1版本中,新增了标注功能.多种标注模式,智能生成,随时查看.原型设计效率更高. Mockplus的标注功能有以下四种模式: 1.无选中标注 在未选中任何组件时,按住Ctrl ...

  8. 用visual studio 2017来调试python

    https://www.visualstudio.com/zh-hans/thank-you-downloading-visual-studio/?sku=Professional&rel=1 ...

  9. 基于注解的接口限流+统一session认证

    代码心得: 一个基本的做法:对于用户身份认证做到拦截器里,针对HandlerMethod进行统一拦截认证,根据方法上的注解标识,判别是否需要身份验证,并将查找出来的User实体存入ThreadLoca ...

  10. mybatis-mysql类型映射

    JDBC Type Java Type CHAR String VARCHAR String LONGVARCHAR String NUMERIC java.math.BigDecimal DECIM ...