【问题标题】:How do I find the largest sequence in a string that is repeated at least once?如何在至少重复一次的字符串中找到最大的序列?
【发布时间】:2012-08-07 20:30:49
【问题描述】:

尝试解决以下问题:

给定一个任意长度的字符串,找出在字符串中出现多次且没有重叠的最长子字符串。

例如,如果输入字符串是ABCABCAB,那么正确的输出应该是ABC。你不能说ABCAB,因为这只会在两个子字符串重叠的地方出现两次,这是不允许的。

对于包含几千个字符的字符串,有什么方法可以相当快速地解决这个问题?

(在有人问之前,这不是功课。我正在寻找优化 Lindenmayer 分形渲染的方法,因为它们往往会花费大量时间来使用幼稚的海龟图形系统在高迭代级别进行绘制。 )

【问题讨论】:

  • ABC 会以某种方式被评为高于BCACAB,它们也具有相同的长度吗?
  • 字符串AAA的答案是什么? AAA?
  • @Piotrek: 'A',因为没有不重叠的多个AA 的集合。
  • @RobotWoods:不是字典顺序;我主要只是以它为例。任何长度相等的子串都应该同样有效。如果您需要决胜局,获胜者是出现次数最多的获胜者。如果您仍然需要决胜局,那么(这完全是任意的)获胜者是第一次迭代在字符串中最早发生的人。

标签: string algorithm language-agnostic


【解决方案1】:

这是一个长度为 11 的字符串的示例,您可以对其进行概括

  • 将块长度设置为 floor(11/2) = 5

  • 扫描剩余 5 个字符的字符串以查找重复。会有3个比较

左右 偏移 偏移 0 5 0 6 1 5
  • 如果您找到重复项,您就完成了。否则将块长度减少到 4 并重复直到块长度变为零。

这是一些(显然未经测试的)伪代码:

String s
int len = floor(s.length/2)
for int i=len; i>0; i--
    for j=0; j<=len-(2*i); j++
        for k=j+i; k<=len-i; k++
            if s.substr(j,j+i) == s.substr(k,k+i)
                return s.substr(j,j+i)
return null

其中可能存在一个错误,但该方法应该是合理的(并且是最小的)。

【讨论】:

  • 我认为这是显而易见的答案。
  • 如果你有ABCXABC呢?您的解决方案不是假设第二次出现在第一次之后吗?无论哪种方式,您的解决方案(至少)是O(length^2),我很确定这不是最佳的。
  • 不,它不假设;它锚定第一个子字符串,然后从第一个子字符串之后的第一个字符开始搜索,比较相同大小的块。我认为没有比 O(n^2) 更好的解决方案......但我可能是错的。
【解决方案2】:

它看起来像一个后缀树问题。创建后缀树,然后找到具有多个子节点的最大压缩分支(在原始字符串中出现多次)。该压缩分支中的字母数应该是最大子序列的大小。

我在这里找到了类似的东西:http://www.coderanch.com/t/370396/java/java/Algorithm-wanted-longest-repeating-substring

看起来可以在 O(n) 内完成。

【讨论】:

  • 我认为这是一个绝妙的主意。你最好为那些不明白的人解释为什么。 +1
  • 我点击链接的帖子到那里的描述性文章,并找到了“最长重复子字符串”的以下示例:“issi' in the case of mississippi'”。不幸的是,这违反了无重叠规则。
  • 是的,它们没有避免重叠的约束。这就是为什么他们走从根到叶的整个路径。如果你不想重叠,你应该只取 一个 压缩 branch,即ssi
【解决方案3】:

首先我们需要定义子字符串的开始符号并定义长度。迭代所有可能的起始位置,然后找出长度,对长度进行二进制搜索(如果你能找到长度为 a 的 substr,你可能会发现长度更长,函数看起来很单调,所以 bin 搜索应该没问题)。然后找到相等的子串是 N,使用 KMP 或 Rabin-Karp 任何线性算法都可以。总计 N*N*log(N)。是不是太复杂了? 代码是这样的:

for(int i=0;i<input.length();++i)
    {
        int l = i;
        int r = input.length();
        while(l <= r)
        {
            int middle = l + ((r - l) >> 1);
            Check if string [i;middle] can be found in initial string. Should be done in O(n); You need to check parts of initial string [0,i-1], [middle+1;length()-1];
            if (found)
                l = middle + 1;
            else
                r = middle - 1;
        }
    }

有意义吗?

【讨论】:

  • 对不起,我不知道那是什么意思。
  • 添加以回答更多 cmets
【解决方案4】:

这种类型的分析通常在基因组序列中进行。看看这篇论文。它具有解决重复问题的有效实现 (c++):http://www.complex-systems.com/pdf/17-4-4.pdf 可能就是你要找的东西

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    相关资源
    最近更新 更多