Trie树和后缀树讲解及应用

Trie后缀树字符串配算法的进阶之路 Trie的基本原理应用场景后缀树的进阶原理构建奥秘如何利用这些数据结构实现高效字符串Trie:字典,用于存储字符串集合的形结构后缀树:压缩Trie,存储字符串所有后缀的优化结构通过Trie的基础建设,到后缀树的空间优化,我们完成字符串配的升级之旅。就像从纸质地图升级到GPS导航,数据结构的选择直接影响算法效率。 阅读详情

后缀数组原理及应用详解

     在pongba的讨论组上看到一道Amazon的面试题:找出给定字符串里的最长回文。例子:输入XMADAMYX。则输出MADAM。这道题的流行解法是用后缀树(Suffix Tree)。这坨数据结构最酷的地方是用它能高效解决一大票复杂的字符串编程问题: 

  • 1.在文本T里查询T是否包含子串P(复杂度同流行的KMP相当)。

    2.文本T里找出最长重复子串。比如abcdabcefda里abc同da都重复出现,而最长重复子串是abc。

    3.找出字符串S1同S2的最长公共子串。注意不是常用作动态规划例子的LCS(最低共有祖先)哈。比如字符串acdfg同akdfc的最长公共子串为df,而他们的LCS是adf。

    4.Ziv-Lampel无损压缩算法。

    5.还有就是这道面试题问的最长回文了。

    虽说后缀树的概念独立于Trie的概念,但我觉得从Trie推出后缀树自然简洁,所以先简单解释一下Trie。它的用途主要是字符串查询。不过词汇变迁多半比较诡异,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。

    继续插播广告。Graph作图软件Graphviz还不错,用的DSL相当简单。上面的图就是用它做的。三步就够了:

    实现Trie数据结构。这步不用花哨。10行代码,一坨hash足矣。

    把上面的结构翻译成Graphviz的DSL。简单的深度优先足矣。

    调用Graphviz的命令。图就生成乐。

    多花20分钟,避免了手工作图排版的自虐行为。而且可以自由试验各式例子而不用担心反复画图的琐碎,何乐而不为嗫?

    有了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$,于是就得到suffix tree乐。

    那后缀树同最长回文有什么关系呢?我们得先知道两坨坨简单概念:

    最低共有祖先,LCA(Lowest Common Ancestor),也就是任意两节点(多个也行)最长的共有前缀。比如下图中,节点7同节点10的共同祖先是节点1与借点,但最低共同祖先是5。 查找LCA的算法是O(1)的复杂度,这年头少见。代价是需要对后缀树做复杂度为O(n)的预处理。 

    广 义后缀树(Generalized Suffix Tree)。传统的后缀树处理一坨单词的所有后缀。广义后缀树存储任意多个单词的所有后缀。例如下图是单词XMADAMYX与XYMADAMX的广义后缀 树。注意我们需要区分不同单词的后缀,所以叶节点用不同的特殊符号与后缀位置配对。 

    有了上面的概念,查找最长回文相对简单了。思维的突破点在于考察回文的半径,而不是回文本身。所谓半径,就是回文对折后的字串。比如回文MADAM 的半径为MAD,半径长度为3,半径的中心是字母D。显然,最长回文必有最长半径,且两条半径相等。还是以MADAM为例,以D为中心往左,我们得到半径 DAM;以D为中心向右,我们得到半径DAM。二者肯定相等。因为MADAM已经是单词XMADAMYX里的最长回文,我们可以肯定从D往左数的字串 DAMX与从D往右数的子串DAMYX共享最长前缀DAM。而这,正是解决回文问题的关键。现在我们有后缀树,怎么把从D向左数的字串DAMX变成后缀 呢?到这个地步,答案应该明显:把单词XMADAMYX翻转就行了。于是我们把寻找回文的问题转换成了寻找两坨后缀的LCA的问题。当然,我们还需要知道 到底查询那些后缀间的LCA。这也简单,给定字符串S,如果最长回文的中心在i,那从位置i向右数的后缀刚好是S(i),而向左数的字符串刚好是翻转S后 得到的字符串S‘的后缀S'(n-i+1)。这里的n是字符串S的长度。有了这套直观解释,算法自然呼之欲出:

    预处理后缀树,使得查询LCA的复杂度为O(1)。这步的开销是O(N),N是单词S的长度

    对单词的每一位置i(也就是从0到N-1),获取LCA(S(i), S(N-i+1)) 以及LCA(S(i+1), S(n-i+1))。查找两次的原因是我们需要考虑奇数回文和偶数回文的情况。这步要考察每坨i,所以复杂度是O(N)

    找到最大的LCA,我们也就得到了回文的中心i以及回文的半径长度,自然也就得到了最长回文。总的复杂度O(n)。

    用上图做例子,i为3时,LCA(3$, 4#)为DAM,正好是最长半径。当然,这只是直观的叙述。

    这篇帖子只大致描述了后缀树的基本思路。要想写出实用代码,至少还得知道下面的知识:

    创建后缀树的O(n)算法。至于是Peter Weiner的73年年度最佳算法,还是Edward McCreight1976的改进算法,还是1995年E. Ukkonen大幅简化的算法,还是Juha Kärkkäinen 和 Peter Sanders2003年进一步简化的线性算法,各位老大随喜。

    实现后缀树用的数据结构。比如常用的子结点加兄弟节点列表,Directed

    优化后缀树空间的办法。比如不存储子串,而存储读取子串必需的位置。以及Directed Acyclic Word Graph,常缩写为黑哥哥们挂在嘴边的DAWG。

                    • 后缀树的用途,总结起来大概有如下几种:
                      (1). 查找字符串o是否在字符串S中。 
                      方案:用S构造后缀树,按在trie中搜索字串的方法搜索o即可。 
                      原理:若o在S中,则o必然是S的某个后缀的前缀。 
                      例如S: leconte,查找o: con是否在S中,则o(con)必然是S(leconte)的后缀之一conte的前缀.有了这个前提,采用trie搜索的方法就不难理解了。 
                      (2). 指定字符串T在字符串S中的重复次数。 
                      方案:用S+’$'构造后缀树,搜索T节点下的叶节点数目即为重复次数 
                      原理:如果T在S中重复了两次,则S应有两个后缀以T为前缀,重复次数就自然统计出来了。 
                      (3). 字符串S中的最长重复子串 
                      方案:原理同2,具体做法就是找到最深的非叶节点。 
                      这个深是指从root所经历过的字符个数,最深非叶节点所经历的字符串起来就是最长重复子串。 
                      为什么要非叶节点呢?因为既然是要重复,当然叶节点个数要>=2。 
                      (4). 两个字符串S1,S2的最长公共部分 

                    • 方案:将S1#S2$作为字符串压入后缀树,找到最深的非叶节点,且该节点的叶节点既有#也有$(无#)。
                      • 转载地址:http://blog.csdn.net/lmh12506/article/details/7975548

                    电能质量仿真:电能质量改善措施仿真_(12).电能质量改善措施综合仿真案例 假设我们有一个微电网系统,该系统包含布式电源(如光伏板、风力发电机)、储能设备、负荷电网连接。电压波动:由于负荷的波动布式电源的间歇性输出,系统电压出现了不稳定的情况。谐波污染:非线性负荷逆变器的运行导致系统中出现了显著的谐波污染。频率偏差:布式电源的波动负荷的突变导致系统频率出现了偏差。我们需要通过仿真来评估这些问题的影响,并提出相应的改善措施。 阅读详情

                    相关推荐

                    智能车图像处理20-进阶篇12--正入十字补线1

                    这篇文章主要讲述基本的正入十字补线方法。

                    taiyuezyh的博客 6796

                    【编程100%】22-05 字符串字符串

                    现在给出一个字符串S,小明要把这个字符串割成两个部,并要求字符串S1是割后左侧字符串的一个子序列,S2是右侧字符串的一个子序列。在此基础上,他希望左右两侧字符串长度的乘积最大。...

                    渡江客涂鸦板 1504

                    OneNET微信小程序源码

                    OneNET微信小程序源码

                    后缀树的生成以及一些运用

                    后缀树的定义 后缀树(Suffix tree)是一种数据结构,能快速解决很多关于字符串问题后缀树的概念最早由Weiner 于1973年提出,既而由McCreight 在1976年Ukkonen在1992年1995年加以改进完善。后缀,顾名思义,甚至通俗点来说,就是所谓后缀就是后面尾巴的意思。比如说给定一长度为n的字符串S=S1S2..Si..Sn,整数i,1 <= i <= n,子

                    Road_To_Fight的博客 891

                    【编程100%】22-08 字符串子序列

                    输入n个字符串,S1,S2,S3,...,Sn。小明想在其中选出K个字符串,把它们按照原始的顺序排好。严格地,小明选出了K个下标1≤i1<i2<i3<,...,<ik≤n,从而得到了新的K个字符串的序列Si1,Si2,Si3,...,Sik。 定义一个字符串序列的价值为所有相邻公共前缀长度之,小明想选出价值最高的序列。...

                    渡江客涂鸦板 686

                    ACwing 刷题 2022/3/14

                    34.链表中环的入口节点 注意,我的错误在于两个指针初始化。错在指把快指针初始化成head->next->next,慢指针初始化成head,仔细想想这样相当于慢指针比快指针少走了一次!!!

                    weixin_53356651的博客 1869

                    S="S1 S2...Sn"是一个长度为N的字符串,存放在一个数组中,编程将S改造之后输出:

                    S="S1 S2…Sn"是一个长度为N的字符串,存放在一个数组中,编程将S改造之后输出: 将S的所有第偶数个字符按照其原来的下标从大到小的次序放在S的后半部; 将S的所有第奇数个字符按照其原来的下标从小到大的次序放在S的前半部; 例如:S=‘ABCDEFGHIJKL’ 则改造后的S为‘ACEGIKLJHFDB’ void RearrangeString() { char ch,s[],s...

                    qq_34752068的博客 6801

                    Trie and Suffix

                    前缀(trie)后缀树(suffix)是两种非常重要的数据结构,往往用来加速字符串查找。 具体的介绍可见下面的资料: http://blog.csdn.net/luowen3405/archive/2011/04/01/6295839.aspx http://blog.csdn.net/TsengYuen/archive/2009/11/16/4815921.aspx   T...

                    Settle Down 300

                    java里面有radix吗_Trie / Radix Tree / Suffix Tree

                    Trie (字典)"A", "to", "tea", "ted", "ten", "i", "in", "inn" 这些单词组成的字典.Radix Tree (基数)基数与字典的区别在于基数将单词压缩了, 节点变得更少Suffix Tree (后缀树)单词 "BANANA" 的后缀树. 每个后缀以$ 结尾. 所有的后缀为A$,NA$,ANA$,NANA$,ANANA$and...

                    weixin_30827827的博客 323

                    Trie(字典)谈到后缀树(10.28修订)

                    Trie(字典)谈到后缀树作者:July、yansha。出处:http://blog.csdn.net/v_JULY_v 。 引言    常关注本blog的读者朋友想必看过此篇文章:从B、B+、B*谈到R ,这次,咱们来讲另外两种:Tire后缀树。不过,在此之前,先来看两个问题。    第一个问题: 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,

                    结构之法 算法之道 17万+

                    前缀后缀树完全攻略:Algorithmic-Resources实战教程

                    在算法与数据结构的世界中,前缀Trie后缀树(Suffix Tree)是处理字符串问题的强大工具。无论是自动补全、拼写检查还是基因序列析,这两种结构都能提供高效的解决方案。本文将带你通过Algorithmic-Resources项目的实战教程,从零开始掌握这两种数据结构的核心原理与应用技巧。 ### 一、前缀Trie):字符串检索的终极利器 前缀,也称为字典,是一种特殊的

                    gitblog_00464的博客 382

                    算法题目二:寻找最长重复子序列(3) 后缀树

                    本文为转载,有所修改。原作者文章为:http://hi.baidu.com/zealot886/item/5d35b442d059701a886d1072(百度空间) 仅为个人学习备份之用途,无任何其他用途!   写这篇文章,主要是因为最近有个课题设计,里面用的字符串配。 学习后缀树之前,先了解一下Trie这个数据结构 Trie是一种搜索,可用于存储并查找字符串

                    1ouis的专栏 1974

                    Trie(字典)谈到后缀树

                    Trie(字典)谈到后缀树 作者:July、yansha。 出处:http://blog.csdn.net/v_JULY_v 。  引言     常关注本blog的读者朋友想必看过此篇文章:从B、B+、B*谈到R ,这次,咱们来讲另外两种:Tire后缀树。不过,在此之前,先来看两个问题。     第一个问题: 一个文本文件,大约有一万行,每

                    千里之行始于足下 557

                    数据结构中的各种浅谈

                    数据结构中为了存储查找的方便,用各种结构来存储文件,本章就浅谈一下各种的表示方法、特点及各自的用途,本章设计的结构包括:二叉查找(二叉排序)、平衡二叉(AVL)、红黑、B-、B+、字典trie)、后缀树、广义后缀树。 1、二叉查找(二叉排序)   (图a) 二叉查找是一种动态查找表(图a),具有这些性质:...

                    weixin_30322405的博客 297

                    广义后缀树(GST)算法的简介

                    一、导言   最近软件安全课上,讲病毒特征码的提取时,老师讲了一下GST算法。这里就做个小总结。   二、简介 基本信息 广义后缀树的英文为Generalized Suffix Tree,简称GST。 算法目的 GST算法的提出是为了解决最大公共子问题,也就是在多个字符串中,找到他们共有的子。 顺便说一句     这个问题听起来最大公共子序列问题(LCS)有些相似,但是二者有...

                    weixin_33939843的博客 1810

                    广义后缀树

                    对于字符串集合T={t1,t2…tn}的广义后缀树,是一个压缩字典trie)其中包含了T中每一个字符串的所有的后缀。 每一个叶节点,是由 对来标记的,即包含了所在的字符串字符串中的开始位置。 ...

                    牛牛码特的博客 429

                    trie(字典)&& 后缀树

                    rie,即字典,又称单词查找或键,是一种形结构,是一种哈希的变种。典型应用是用于统计排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。 Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

                    zhu's 专栏 1066

                    Trie

                    一、Tire的概念 二、Trie应用{Trie字符串统计 最大异或对(难点)[暴力遍历法 Trie优化法]}

                    咸鱼势力登场 894

                    后缀树(一)定义及构造

                    后缀树(Suffix tree): :一个具有m个词的字符串S的后缀树T,就是一个包含一个根节点的有向,该恰好带有m个叶子,这些叶子被赋予从1到m的标号。每一个内部节点,除了根节点以外,都至少有两个子节点,而且每条边都用$的一个非空子来标识。出自同一节点的任意两条边的标识不会以相同的词开始。后缀树的关键特征是:对于任何叶子i,从根节点到该叶子所经历的边的所有标识联起来后恰好拼出S的从i...

                    )梦想之深邃( 2408
                    上一篇: 2路插入排序实现
                    下一篇: 图相似性相关论文阅读总结
                    dota爱好者
                    博客等级 码龄16年 89粉丝 109原创
                    评论
                    添加红包

                    请填写红包祝福语或标题

                    红包个数最小为10个

                    红包金额最低5元

                    当前余额3.43前往充值 >
                    需支付:10.00
                    成就一亿技术人!
                    领取后你会自动成为博主和红包主的粉丝 规则
                    hope_wisdom
                    发出的红包
                    实付
                    使用余额支付
                    点击重新获取
                    扫码支付
                    钱包余额 0

                    抵扣说明:

                    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                    余额充值