ref : https://dsqiu.iteye.com/blog/1705697

1.Trie导引

Trie树是一种基于树的数据结构,又称单词查找树、前缀树,是一种哈希树的变种。应用于字符串的统计与排序,经常被搜索引擎系统用于文本词频统计。用于存储字符串以便支持快速模式匹配,主要应用在信息检索中,Trie支持的主要查询操作是模式匹配和前缀匹配。Trie树可以看着是一个确定有限状态自动机,有限状态自动机另一篇博文字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法一网打尽 有介绍。

2.标准Trie

令S是取自字母表∑的s个集合,满足S中不存在一个串是另一个串的前缀。S的一个标准Trie(standard trie)是一颗有序书T,满足如下性质:

  • 除了根之外,T中的每个结点标记有∑的一个字符。
  • T中一个内部结点的子结点的次序有字母表∑上的规范次序确定。
  • T有s个外部结点(叶结点),每个外部结点关联S中的一个串,满足从根到T中一个外部结点v的路径上标记连接产生S中关联的一个串。

下图是串{bear,bell,bid,bull,buy,sell,stock,stop}的标准Trie

存储总长为n,来自大小为d的字母表中s个串的集合S的标准Trie具有如下性质:

  1. T中每个内部结点最多有d个子结点。
  2. T有s个外部结点。
  3. T的高度等于最长串的长度。
  4. T中的结点书为O(n)。

性能:对于有n个英文字母的串来说,在内部结点中定位指针所需要花费O(d)时间,d为字母表的大小,英文为26。由于在上面的算法中内部结点指针定位使用了数组随机存储方式,因此时间复杂度降为了O(1)。但是如果是中文字,下面在实际应用中会提到。因此我们在这里还是用O(d)。 查找成功的时候恰好走了一条从根结点到叶子结点的路径。因此时间复杂度为O(d*n)。但是,当查找集合X中所有字符串两两都不共享前缀时,trie中出现最坏情况。除根之外,所有内部结点都自由一个子结点。此时的查找时间复杂度蜕化为O(d*(n^2))

中文词语的标准Trie树

由于中文的字远比英文的26个字母多的多。因此对于trie树的内部结点,不可能用一个26的数组来存储指针。如果每个结点都开辟几万个中国字的指针空间。估计内存要爆了,就连磁盘也消耗很大。

一般我们采取这样种措施:

(1) 以词语中相同的第一个字为根组成一棵树。这样的话,一个中文词汇的集合就可以构成一片Trie森林。这篇森林都存储在磁盘上。森林的root中的字和root所在磁盘的位置都记录在一张以Unicode码值排序的有序字表中。字表可以存放在内存里。

(2) 内部结点的指针用可变长数组存储。

特点:由于中文词语很少操作4个字的,因此Trie树的高度不长。查找的时间主要耗费在内部结点指针的查找。因此将这项指向字的指针按照字的Unicode码值排序,然后加载进内存以后通过二分查找能够提高效率。

标准Trie的应用举例

标准Trie的应用和优缺点

(1) 全字匹配:确定待查字串是否与集合的一个单词完全匹配。

(2) 前缀匹配:查找集合中与以s为前缀的所有串。

注意:Trie树的结构并不适合用来查找子串。这一点和前面提到的PAT Tree以及后面专门要提到的Suffix Tree的作用有很大不同。

优点: 查找效率比与集合中的每一个字符串做匹配的效率要高很多。在o(m)时间内搜索一个长度为m的字符串s是否在字典里。

缺点:标准Trie的空间利用率不高,可能存在大量结点中只有一个子结点,这样的结点绝对是一种浪费。正是这个原因,才迅速推动了下面所讲的压缩trie的开发。

压缩Trie

压缩Trie类似于标准Trie,但它能保证Trie中的每个内部结点至少有两个字结点。通过把单子结点链压缩进各条边中来执行这个规则。设T是一棵标准Trie,如果T的一个内部结点v有一个子结点且它不是根,则称为这个内部结点是内部结点是冗余的(redundant)。

如果

串{bear,bell,bid,bull,buy,sell,stock,stop}的压缩Trie

压缩Trie的性质和优势:

与标准Trie比较,压缩Trie的结点数与串的个数成正比了,而不是与串的总长度成正比。一棵存储来自大小为d的字母表中的s个串的结合T的压缩trie具有如下性质:

(1) T中的每个内部结点至少有两个子结点,至多有d个子结点。

(2) T有s个外部结点。

(3) T中的结点数为O(s)

存储空间从标准Trie的O(n)降低到压缩后的O(s),其中n为集合T中总字符串长度,s为T中的字符串个数。

压缩Trie应用举例

假定串的集合S是S[0],S[1],...,S[s-1]的一个数组,使用三元组(i,j,k)隐式地表示存储的标记X,满足X=S[i][j,...,k];即X是S[i]的子串,由从j到k所包含的字符组成。

后缀Trie

后缀Trie的字符串集合是由指定字符串的后缀子串构成的。比如、完整字符串"minimize"的后缀子串组成的集合S分别如下:

s1=minimize

s2=inimize

s3=nimize

s4=imize

s5=mize

s6=ize

s7=ze

s8=e

然后把这些子串的公共前缀作为内部结点构成一棵"minimize"的后缀树,如下图所示:

节省空间

因为长度为n的串X的后缀总长度为n(n+1)/2,显式存储X的所有后缀所需空间为O(n²)。而后缀Trie隐式地表示这些串所需空间为O(n)。

后缀Trie创建(图示)

当插入子串时,发现叶子结点中的关键字与子串有公共前缀,则需要将该叶子结点分裂。如上图第3到4步。否则,重新创建一个叶子结点来存放后缀,如上图第1到2步。

Suffix Trie的子串查询

如果在后缀树T中查找子串P,我们需要这样的过程:

(1) 从根结点root出发,遍历所有的根的孩子结点:N1,N2,N3....

(2) 如果所有孩子结点中的关键字的第一个字符都和P的第一个字符不匹配,则没有这个子串,查找结束。

(3) 假如N3结点的关键字K3第一个字符与P的相同,则匹配K3和P。

若 K3.length>=P.length  并且K3.subString(0,P.length-1)=P,则匹配成功,否则匹配失败。

若 K3.length<=P.length  并且K3=P.subString(0,
K3.length-1),则将子串P1=P.subString(K3.length, P.length);
即取出P中排除K3之后的子串。然后P1以N3为根结点继续重复(1)~   (3)的步骤。直到匹配完P1的所有字符,则匹配成功。否则匹配失败。

查询效率:很显然,在上面的算法中。匹配成功正好比较了P.length次字符。而定位结点的孩子指针,和Trie情况类似,假如字母表数量为d。则查询效率为O(d*m),实际上,d是固定常数,如果使用Hash表直接定位,则d=1.

因此,后缀树查询子串P的时间复杂度为O(m),其中m为P的长度。但是构造后缀Trie的时间为O(dn)。

后缀Trie应用

标准Trie树只适合前缀匹配和全字匹配,并不适合后缀和子串匹配。而后缀Trie在这方面则非常合适。

参考:

①Michael T. Goodrich Roberto Tamassia Algorithm Design Foundations, Analysis, and Internet Examples

②Heart.X.Raid: http://hxraid.iteye.com/blog/618962

③Heart.X.Raid: http://hxraid.iteye.com/blog/620414

标准Trie、压缩Trie、后缀Trie的更多相关文章

  1. Trie三兄弟——标准Trie、压缩Trie、后缀Trie

    1.Trie导引 Trie树是一种基于树的数据结构,又称单词查找树.前缀树,字典树,是一种哈希树的变种.应用于字符串的统计与排序,经常被搜索引擎系统用于文本词频统计.用于存储字符串以便支持快速模式匹配 ...

  2. 【BZOJ-4212】神牛的养成计划 Trie树 + 可持久化Trie树

    4212: 神牛的养成计划 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 136  Solved: 27[Submit][Status][Discus ...

  3. [转载]字典树(trie树)、后缀树

    (1)字典树(Trie树) Trie是个简单但实用的数据结构,通常用于实现字典查询.我们做即时响应用户输入的AJAX搜索框时,就是Trie开始.本质上,Trie是一颗存储多个字符串的树.相邻节点间的边 ...

  4. 字典树(trie树) 后缀树 广义后缀树

    转自:http://www.cnblogs.com/dong008259/archive/2011/11/11/2244900.html (1)字典树(Trie树) Trie是个简单但实用的数据结构, ...

  5. bzoj 2251: 外星联络 后缀Trie

    题目大意 http://www.lydsy.com/JudgeOnline/problem.php?id=2251 题解 本来以为这道题应该从01序列的性质入手 结果就想歪了 等自己跳出了01序列这个 ...

  6. trie上构建后缀数组

    例题: 往事太多,有时候忘了就忘了吧. 如果有非记不可的,就只能用点附加手段啦! 我们定义一棵往事树是一个 n 个点 n-1 条边的有向无环图,点编号为 1到 n,其中 1 号点被称为是根结点,除根结 ...

  7. Leetcode: Implement Trie (Prefix Tree) && Summary: Trie

    Implement a trie with insert, search, and startsWith methods. Note: You may assume that all inputs a ...

  8. 【BZOJ4212】神牛的养成计划 Trie树+可持久化Trie树

    [BZOJ4212]神牛的养成计划 Description Hzwer成功培育出神牛细胞,可最终培育出的生物体却让他大失所望...... 后来,他从某同校女神 牛处知道,原来他培育的细胞发生了基因突变 ...

  9. [Swift]LeetCode208. 实现 Trie (前缀树) | Implement Trie (Prefix Tree)

    Implement a trie with insert, search, and startsWith methods. Example: Trie trie = new Trie(); trie. ...

随机推荐

  1. 2018-2019-2 网络对抗技术 20165202 Exp8 Web基础

    博客目录 一.实践内容 Web前端HTML 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HTML Web前端javascipt 理解JavaScr ...

  2. python文件导出exe可执行程序

    开门见山的说: 1.安装pyinstaller.(windows 用pip3 Mac 用pip)在cmd中输入:pip3 install pyinstaller 2.找到你要打包的文件的目录的上一个目 ...

  3. 创建Bitmap之Bitmap静态方法使用示例

    package com.loaderman.customviewdemo; import android.app.Activity; import android.content.Intent; im ...

  4. Python 3.x 引入了函数注释

    Python 3.x 引入了函数注释,以增强函数的注释功能,下面是一个普通的自定义函数:   def dog(name, age, species):   return (name, age, spe ...

  5. Linux -- 信号编程

    进程捕捉到信号对其进行处理时,进程正在执行的正常序列就被信号处理程序临时中断,它首先执行该信号处理程序中的指令.如果从信号处理程序返回(例如没有调用exit或longjmp),则继续执行在捕捉到信号时 ...

  6. python基础之坑爹正则表达式

    python基础之坑爹正则表达式 概述 re模块就是python语言中的正则表达式,拆出来单独写一条blog是因为正则表达式本身就是比较庞大的知识,写具体些让自己以后方便查找. IP: ^(25[0- ...

  7. winform软件版本检测自动升级开发流程(转)

    注:按此博文试验OK 基于C/S的开发有开发效率高,对于业务逻辑复杂,且不需要外网使用具有较大优势,但是弊端也不可忽视,就是升级麻烦,不可能每写一个版本就要拿着安装包给每个人去替换,这样不仅搞得自己很 ...

  8. Python - Django - 使用 Bootstrap 样式修改书籍列表

    展示书籍列表: 首先修改原先的 book_list.html 的代码: <!DOCTYPE html> <!-- saved from url=(0042)https://v3.bo ...

  9. LODOP在页面让客户选择打印机

    获取打印机列表可以放在onload事件里,如过当前是使用的c-lodop,由于websoket链接需要时间,一进入页面可能会报错,被准备好或网页没下载完成等,也可以在点击事件里让用户获取打印机.之前写 ...

  10. 使用JavaScript实现量化策略并发执行

    本文代码和文章发在FMZ发明者比特币量化交易平台上: 使用JavaScript实现量化策略并发执行--封装Go函数 - 发明者量化 https://www.fmz.com/digest-topic/3 ...