数据结构——后缀树
来源:互联网 发布:淘宝上买车保险便宜吗 编辑:程序博客网 时间:2024/06/08 03:04
1、简介
后缀树(Suffix tree)是一种数据结构,能快速解决很多关于字符串的问题。后缀树提出的目的是用来支持有效的字符串匹配和查询。
一个具有m个词的字符串S的后缀树T,就是一个包含一个根节点的有向树,该树恰好带有m个叶子,这些叶子被赋予从1到m的标号。 每一个内部节点,除了根节点以外,都至少有两个子节点,而且每条边都用$的一个非空子串来标识。出自同一节点的任意两条边的标识不会以相同的词开始。后缀树的关键特征是:对于任何叶子i,从根节点到该叶子所经历的边的所有标识串联起来后恰好拼出S的从i位置开始的后缀,即S[i,…,m]。树中节点的标识被定义为从根到该节点的所有边的标识的串联。
2、推导
虽说后缀树的概念独立于Trie的概念,但从Trie推出后缀树自然简洁,所以先简单解释一下Trie。“Trie”这个单词来自于"retrieve",可见它的用途主要是字符串查询。不过词汇变迁多半比较诡异,Trie不发tree的音,而发try的音。
Trie是个简单但实用的数据结构,通常用于实现字典查询。我们做即时响应用户输入的AJAX搜索框时,就是Trie开始。本质上,Trie是一颗存储多个字符串的树。相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树 不同的地方是,相同的字符串前缀共享同一条分支。还是例子最清楚。给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:
可以看出:
- 每条边对应一个字母。
- 每个节点对应一项前缀。叶节点对应最长前缀,即单词本身。
- 单词inn与单词int有共同的前缀“in”, 因此他们共享左边的一条分支,root->i->in。同理,ate, age, adv, 和ant共享前缀"a",所以他们共享从根节点到节点"a"的边。
- 查询非常简单。比如要查找int,顺着路径i -> in -> int就找到了。
搭建Trie的基本算法也很简单,无非是逐一把每则单词的每个字母插入Trie。插入前先看前缀是否存在。如果存在,就共享,否则创建对应的节点和边。比如要插入单词add,就有下面几步:
- 考察前缀"a",发现边a已经存在,于是顺着边a走到节点a。
- 考察剩下的字符串"dd"的前缀"d",发现从节点a出发,已经有边d存在,于是顺着边d走到节点ad。
- 考察最后一个字符"d",这下从节点ad出发没有边d了,于是创建节点ad的子节点add,并把边ad->add标记为d。
有了Trie,后缀树就容易理解了。先说说后缀的定义:给定一长度为n的字符串S=S1S2..Si..Sn,和整数i,1 <= i <= n,子串SiSi+1...Sn都是字符串S的后缀。以字符串S=XMADAMYX为例,它的长度为8,所以S[1..8], S[2..8], ... , S[8..8]都算S的后缀,我们一般还把空字串也算成后缀。对于后缀S[i..n],我们说这项后缀起始于i,这样,我们一共有如下后缀:
- S[1..8], XMADAMYX, 也就是字符串本身,起始位置为1。
- S[2..8], MADAMYX,起始位置为2。
- S[3..8], ADAMYX,起始位置为3。
- S[4..8], DAMYX,起始位置为4。
- S[5..8], AMYX,起始位置为5。
- S[6..8], MYX,起始位置为6。
- S[7..8], YX,起始位置为7。
- S[8..8], X,起始位置为8。
- 空字串。记为$。
而后缀树,就是包含一则字符串所有后缀的压缩Trie。把上面的后缀加入Trie后,我们得到下面的结构:
仔细观察上图,我们可以看到不少值得压缩的地方。比如蓝框标注的分支都是独苗,没有必要用单独的节点同边表示。如果我们允许任意一条边里包含多个字 母,就可以把这种没有分叉的路径压缩到一条边。另外每条边已经包含了足够的后缀信息,我们就不用再给节点标注字符串信息了。我们只需要在叶节点上标注上每 项后缀的起始位置。于是我们得到下图:
这样的结构丢失了某些后缀。比如后缀X在上图中消失了,因为它正好是字符串XMADAMYX的前缀。为了避免这种情况,我们也规定每项后缀不能是其它后缀的前缀。要解决这个问题其实挺简单,在待处理的子串后加一空字串就行了。例如我们处理XMADAMYX前,先把XMADAMYX变为XMADAMYX$,于是就得到后缀树了。
3、应用场景
后缀树有以下几种应用场景:
- 在文本T里查询T是否包含子串P(复杂度同流行的KMP相当)。
- 文本T里找出最长重复子串。比如abcdabcefda里abc同da都重复出现,而最长重复子串是abc。
- 找出字符串S1同S2的最长公共子串。注意不是常用作动态规划例子的LCS哈。比如字符串acdfg同akdfc的最长公共子串为df,而他们的LCS是adf。
- Ziv-Lampel无损压缩算法。
- 找出给定字符串里的最长回文。
- 生物信息学里应该应用广泛。碱基匹配和选取的计算本质上就是操作超长的{C, T, A, G, U}*字符串嘛。
0 0
- 数据结构——后缀树
- 数据结构——前缀树 后缀树 后缀数组
- 数据结构系列——Java后缀树实现代码
- 数据结构实践——后缀表达式(栈)
- 数据结构实践——后缀表达式
- 【数据结构】后缀表达式-->表达式树
- 第六周数据结构实践——后缀表达式(栈)【项目5 - 后缀表达式】
- 数据结构系列——后缀树(附Java实现代码)
- ACM常用模板——数据结构——后缀数组
- 数据结构——从中缀向后缀转换表达式
- 第六周 数据结构实践——后缀式表达
- 洛谷 1449——后缀表达式(线性数据结构)
- 第6周SHH数据结构—【项目5 - 后缀表达式】
- 数据结构 中缀转后缀 后缀转二叉树并打印 后缀计算值
- 数据结构6————栈的应用3-后缀表达式
- 基于java的数据结构学习手记5——后缀表达式生成与计算
- 重学数据结构006——中缀表达式转后缀表达式
- 数据结构与算法复习(10)—— 后缀数组与字符串问题
- 【shell】Here文档自动生成Makefile
- 文章标题
- jsp页面传递date到java报错
- android 隐藏虚拟按键
- 强哥收藏的Android开源库集合【UI效果篇】
- 数据结构——后缀树
- ES 5.3 集群安装
- Spring Boot 热部署之spring-boot-devtools
- Java十进制转化成二进制方法
- Android Gradle学习1--android studio中的相关配置
- 动画函数测试封装
- UNIX环境高级编程fcntl和dup
- JDK1.5~JDK1.7新特性
- C++N4 作业(项目二、三、四)