【问题标题】:Longest substring that appears at least twice in O(n.logn)在 O(n.logn) 中至少出现两次的最长子串
【发布时间】:2021-10-27 21:09:21
【问题描述】:

问题:

给定一个包含 N 个字符的字符串 S (N <= 200 000),求出现至少两次的最长子字符串的长度(子字符串可以重叠)。

我的解决方案:

这是我尝试过的:

int main()
{
    std::string s;
    std::cin >> s;
    int max = 0;
    typedef std::string::const_iterator sit;
    sit end = s.end();
    for(sit it1 = s.begin(); it1 != end; ++it1)
        for(sit it2 = it1 + 1; it2 != end; ++it2)
            max = std::max(max, std::mismatch(it1, it1 + (end - it2), it2).first - it1);
    std::cout << max;
}

问题:

但是,上述解决方案将在 O(n^3) 中运行时获得 TLE。有什么方法可以改进它,使它可以在 O(n.logn) 中运行?

【问题讨论】:

  • “因为它在 O(n^2) 中运行”。是O(n^3),你忘了数std::mismatch

标签: c++ string algorithm substring longest-substring


【解决方案1】:

找出至少出现两次的最长子串的长度 (子串可以重叠)

这个问题也俗称Longest repeated substring problem。 可以用后缀树在线性时间内求解。

解决这个问题:

  1. 在给定的字符串 S) 中添加一个特殊字符 '$',
  2. 从 S 构建后缀树;
  3. S 的最长重复子串由后缀树中最深的内部节点表示,其中深度由从根开始遍历的字符数来衡量。

时间复杂度:

  • 后缀树需要 O(nlog(k))) 时间,其中 k 是字母表的大小(如果 k 被认为是常数,则渐近行为是线性的)
  • 树遍历(寻找最长的重复子串)可以在 O(n) 时间内完成

【讨论】:

    【解决方案2】:

    后缀树对于这个问题来说太过分了。事实上,二分查找就足够了,实现起来也容易得多。

    想法

    这个想法很简单:如果存在长度为 N (N > 1) 的重复子串,那么也必然存在长度为 N - 1 的子串。因此,如果我们让 f(x) 表示一个返回 true 的函数如果存在长度为 x 的重复子串,f(x) 将是一个单调函数,它允许二分查找解决方案。

    实施

    对最长重复子串的长度进行二分搜索并应用sliding windows 来检查给定长度是否可能。使用字符串散列来检查重复项。 复杂度:N log N

    【讨论】:

    • 用于重复检查的二叉树将不起作用,因为每次比较需要 O(N),所以总数≥ O(N²)。
    猜你喜欢
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-17
    相关资源
    最近更新 更多