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

本文为转载,有所修改。原作者文章为:http://hi.baidu.com/zealot886/item/5d35b442d059701a886d1072(百度空间)

仅为个人学习备份之用途,无任何其他用途!

 

写这篇文章,主要是因为最近有个课题设计,里面用的字符串匹配。

学习后缀树之前,先了解一下Trie这个数据结构

Trie是一种搜索树,可用于存储并查找字符串。Trie每一条边都对应一个字符。在Trie中查找字符串S时,只要按顺序枚举S的各个字符,从Trie的根节点开始选择相应的边走,如果枚举完的同时恰好走到Trie树的叶子节点,说明S存在于Trie中。如果未到达叶子节点,或者枚举中未发现相应的边,则S没有被包含在Trie中。





后缀树就是一种压缩后的Trie树。
比如 S:banana,对S建立后缀树。
首先给出S的后缀们
0:banana
1:anana
2:nana
3:ana
4:na
5:a
6:空
为了更清楚的表示后缀,我们在后缀的后面加上$
0:banana$
1:anana$
2:nana$
3:ana$
4:na$
5:a$
6:$

然后对其进行分类:
5: a$
3: ana$
1: anana$

0: banana$

4: na$
2: nana$

6: $

后缀树的应用:
example 1:在树中查找an(查找子字符串)
example 2:统计S中出现字符串T的个数
没出现一次T,都对应着一个不同的后缀,而这些后缀们又对应着同一个前缀T,因此这些后缀必定都属于同一棵子树,这棵子树的分支数就是T在S中出现的次数。


example 3:找出S中最长的重复子串,所谓重复子串,是指出现了两次以上
首先定义节点的 ”字符深度“ = 从后缀树根节点到每个节点所经过的字符串总长。找出有最大字符深度的非叶节点。则从根节点到该非叶节点所经过的字符串即为所求。

后缀树的存储:
为了节省空间,我们不在边上存储字符串,而是存储该字符串在原串中的起止位置。空间复杂度O(n)

后缀树的构造:
最简单的方法,使用Trie的构造方法,时间复杂度为O(n^2)
后缀树也可以在O(n)的时间复杂度内构造,但比较复杂

基本思路:先向后缀树中插入最长的后缀串(S本身),其次插入次长的后缀串,以此类推,最后插入空串。
定义后缀链接(Suffix Link)=从节点A指向节点B的指针,B所表示的子串是A所表示的子串的最长后缀。既,根节点到A所经过的字符串s=aw,则从根节点到B所经过的字符串为w。

算法所用符号描述:

后缀树的构造,算法流程:
1)定义SL(root)=root,首先插入S,此时后缀树仅有两个节点。
2)设已经插入了S(i),现在要插入S(i+1),分两种情况讨论:
1:P(S(i))在插入之前已经存在,(如,na,ana,a是na的parent),则P(S(i))有后缀链接,令u=SL(P(S(i))),从u开始沿着树往下查找,在合适的地方插入
2:P(S(i))是插入S(i)过程中产生的,此时G(S(i))必定存在并有后缀链接,比如(na,ana,bana),令u=SL(G(S(i))),w=W(G(S(i)),P(S(i))).从u开始,对w进行快速定位,并找到节点v(v可能需要分割边来得到)。令SL(G(S(i)))指向v,从v开始沿着树往下查找,在合适的地方插入新的节点
不断重复以上步骤,即可完成后缀树的构造。


 

    • 2,后缀树的用途,总结起来大概有如下几种 

 

    • (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$作为字符串压入后缀树,找到最深的非叶节点,且该节点的叶节点既有#也有$(无#)。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值