【问题标题】:how to get longest repeating string in substring from suffix tree如何从后缀树中获取子字符串中最长的重复字符串
【发布时间】:2015-10-06 06:19:31
【问题描述】:

我需要在子字符串中找到最长的重复字符串。假设我有字符串"bannana"

维基百科says关注:

在计算机科学中,最长的重复子串问题是 查找出现在的字符串的最长子字符串的问题 至少两次。在带有字符串“ATCGATCGA$”的图中,最长的 重复的子串是“ATCGA”

所以我假设字符串 "bannana" 有两个同样长的子字符串(如果不正确,请指正):"an""na"

Wikipedia 还 says 为此使用了后缀树。更具体地说,here 是引用如何做到这一点(在我看来,这比wikipedia 上的定义更容易理解):

建立一个后缀树,然后找到至少有 2 个的最高节点 后代。

我发现了几种后缀树的实现。以下代码取自here

use strict;
use warnings;
use Data::Dumper;

sub classify {
    my ($f, $h) = (shift, {});
    for (@_) { push @{$h->{$f->($_)}}, $_ }
    return $h;
}
sub suffixes {
    my $str = shift;
    map { substr $str, $_ } 0 .. length($str) - 1;
}
sub suffix_tree {
    return +{} if @_ == 0;
    return +{ $_[0] => +{} } if @_ == 1;
    my $h = {};
    my $classif = classify sub { substr shift, 0, 1 }, @_;
    for my $key (sort keys %$classif) {
        my $subtree = suffix_tree(
            grep "$_", map { substr $_, 1 } @{$classif->{$key}}
        );
        my @subkeys = keys %$subtree;
        if (@subkeys == 1) {
            my $subkey = shift @subkeys;
            $h->{"$key$subkey"} = $subtree->{$subkey};
        } else { $h->{$key} = $subtree }
    }
    return $h;
}

print +Dumper suffix_tree suffixes 'bannana$';

对于字符串"bannana",它返回以下树:

$VAR1 = {
          '$' => {},
          'n' => {
                   'a' => {
                            'na$' => {},
                            '$' => {}
                          },
                   'nana$' => {}
                 },
          'a' => {
                   '$' => {},
                   'n' => {
                            'a$' => {},
                            'nana$' => {}
                          }
                 },
          'bannana$' => {}
        };

另一个实现来自here,对于字符串"bannana",它返回以下树:

 7: a
 5: ana
 2: annana
 1: bannana
 6: na
 4: nana
 3: nnana

     |(1:bannana)|leaf
tree:|
     |      |(4:nana)|leaf
     |(2:an)|
     |      |(7:a)|leaf
     |
     |     |(4:nana)|leaf
     |(3:n)|
     |     |(5:ana)|leaf
3 branching nodes

问题:

  1. 如何从这些图表中获取 "an""na" 字符串?
  2. 您可以看到树是不同的,它们是否等效,如果是,为什么它们不同,如​​果不是,哪种算法是正确的?
  3. 如果 perl 实现错误,是否有任何适用于 perl/python 的实现?
  4. 我已经阅读了关于 Ukkonen 算法的信息,该算法在第二个示例的页面上也有提及(我没有了解在线版本是否使用此算法),是否有任何提到的示例使用此算法?如果不是,与 Ukkonen 相比,使用的算法是否更慢或有任何缺点?

【问题讨论】:

  • 尚不清楚第一个实现如何将bannana 转换为banana
  • 第一个实现是可疑的:是bannana 还是banana?第二个看起来不对:它有 5 个叶子,但 bannana 有 7 个字母,所以根据定义,它应该有 7 个叶子。
  • 您的符号也令人困惑。后缀树通常标记边缘,而不是节点。但是您似乎标记了节点,那么您的标签代表什么?
  • 对不起,我的错我已经解决了 bannanabanana。这是bannana@IVlad 老实说我不知道​​。我最初的目标是找到最长的重复子串,后缀树只是一个“工具”,我不知道它们到底是如何工作的。但我的理解是,这就是我的问题的答案。
  • 你应该做的是查看从后缀树计算最长公共前缀数组(通常与后缀数组一起使用)的算法。

标签: string algorithm perl tree suffix-tree


【解决方案1】:

1.我怎样才能从这些图中得到“an”和“na”字符串?

构建后缀树,然后找到具有至少 2 个后代的最高节点。

string-node 是从根到该节点的每个节点的连接字符串。 highest node 是最大长度的节点string-node

在我的第二个问题的答案中查看树。 (3:n) 有 2 个后代,节点路径是 (2:a)->(3:n),连接是 an。也为(5:a) 获取na

2.如您所见,树是不同的,它们是否等效,如果是,为什么它们不同,如​​果不是,哪种算法是正确的?

这些树是不同的。为字符串 "bannana$" 重建第二棵树( 和第一棵树一样):

 8: $
 7: a$
 5: ana$
 2: annana$
 1: bannana$
 6: na$
 4: nana$
 3: nnana$

     |(1:bannana$)|leaf
tree:|
     |     |     |(4:nana$)|leaf
     |     |(3:n)|
     |     |     |(7:a$)|leaf
     |(2:a)|
     |     |(8:$)|leaf
     |
     |     |(4:nana$)|leaf
     |(3:n)|
     |     |     |(6:na$)|leaf
     |     |(5:a)|
     |     |     |(8:$)|leaf
     |
     |(8:$)|leaf
5 branching nodes

3.如果 perl 实现是错误的,perl/python 是否有任何可行的实现?

我不懂 Perl,但树是正确构建的。

4.我已经阅读了关于 Ukkonen 算法的内容,该算法在第二个示例页面上也有提及(我没有发现在线版本是否使用此算法),是否有任何提到的示例使用此算法?如果不是,与 Ukkonen 相比,使用的算法是否更慢或有任何缺点?

我之前说过我不知道 Perl,但它是第一个算法中的一行意味着它至少可以工作 O(n^2)n 它是长度字符串):

map { substr $str, $_ } 0 .. length($str) - 1;

Ukkonen 的算法工作线性时间O(n)

第一个算法也是递归的,可能会影响使用的内存。

【讨论】:

    猜你喜欢
    • 2015-11-04
    • 2013-05-10
    • 1970-01-01
    • 1970-01-01
    • 2013-08-12
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 2016-02-28
    相关资源
    最近更新 更多