You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以O(n·logn)复杂度求解至少出现两次的最长子串长度

问题重述

给定长度为N(N ≤ 200000)的字符串S,求至少出现两次的最长子串的长度,子串允许重叠。原暴力实现的时间复杂度为O(n³),无法处理大规模数据,需要优化到O(n·logn)级别。

优化方案1:二分答案 + 滚动哈希(Rabin-Karp)

思路

  • 二分枚举可能的子串长度L,每次判断是否存在至少两个长度为L的相同子串:
    • 若存在,说明可以尝试更长的子串,调整二分左边界
    • 若不存在,说明需要缩短长度,调整二分右边界
  • 判断环节使用滚动哈希预处理所有长度为L的子串的哈希值,将哈希值排序后检查是否有重复值即可,单次判断时间复杂度为O(n)
  • 整体二分次数为O(logn),总时间复杂度为O(n·logn)

代码实现

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

typedef unsigned long long ull;
const int BASE = 131;
vector<ull> hash_val, power;

void init(const string& s) {
    int n = s.size();
    hash_val.resize(n + 1, 0);
    power.resize(n + 1, 1);
    for (int i = 0; i < n; i++) {
        hash_val[i + 1] = hash_val[i] * BASE + s[i];
        power[i + 1] = power[i] * BASE;
    }
}

ull get_hash(int l, int r) { // 区间左闭右开,子串长度为r-l
    return hash_val[r] - hash_val[l] * power[r - l];
}

bool check(int L, int n) {
    if (L == 0) return true;
    vector<ull> hashes;
    for (int i = 0; i + L <= n; i++) {
        hashes.push_back(get_hash(i, i + L));
    }
    sort(hashes.begin(), hashes.end());
    for (int i = 1; i < hashes.size(); i++) {
        if (hashes[i] == hashes[i - 1]) return true;
    }
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s;
    cin >> s;
    init(s);
    int n = s.size();
    int left = 0, right = n - 1, ans = 0;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (check(mid, n)) {
            ans = mid;
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    cout << ans << endl;
    return 0;
}

注:如果担心ull自然溢出的哈希碰撞问题,可以使用双模数哈希进一步降低碰撞概率,不会改变时间复杂度级别。

优化方案2:后缀数组 + 最长公共前缀(LCP)数组

思路

  • 后缀数组是将字符串的所有后缀按字典序排序后得到的下标数组
  • 两个后缀的最长公共前缀长度,就是这两个后缀对应公共子串的最大长度。而排序后,最长的公共前缀一定出现在相邻的两个后缀中,因此只需要计算相邻后缀的LCP,取最大值即可得到答案
  • 用倍增法构建后缀数组的时间复杂度为O(n·logn),计算LCP数组的时间复杂度为O(n),总复杂度满足要求

代码实现

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> sa, rk, height;

void build_sa(const string& s) {
    int n = s.size(), m = max(256, n);
    sa.resize(n), rk.resize(n), height.resize(n);
    vector<int> x(n), y(n), cnt(max(n, m));
    for (int i = 0; i < n; i++) cnt[x[i] = s[i]]++;
    for (int i = 1; i < m; i++) cnt[i] += cnt[i - 1];
    for (int i = n - 1; i >= 0; i--) sa[--cnt[x[i]]] = i;
    for (int k = 1; k <= n; k <<= 1) {
        int p = 0;
        for (int i = n - k; i < n; i++) y[p++] = i;
        for (int i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k;
        fill(cnt.begin(), cnt.begin() + m, 0);
        for (int i = 0; i < n; i++) cnt[x[y[i]]]++;
        for (int i = 1; i < m; i++) cnt[i] += cnt[i - 1];
        for (int i = n - 1; i >= 0; i--) sa[--cnt[x[y[i]]]] = y[i];
        swap(x, y);
        p = 1; x[sa[0]] = 0;
        for (int i = 1; i < n; i++) {
            x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) ? p - 1 : p++;
        }
        if (p >= n) break;
        m = p;
    }
}

void build_height(const string& s) {
    int n = s.size(), k = 0;
    for (int i = 0; i < n; i++) rk[sa[i]] = i;
    for (int i = 0; i < n; i++) {
        if (rk[i] == 0) continue;
        if (k) k--;
        int j = sa[rk[i] - 1];
        while (i + k < n && j + k < n && s[i + k] == s[j + k]) k++;
        height[rk[i]] = k;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string s;
    cin >> s;
    build_sa(s);
    build_height(s);
    int ans = 0;
    for (int i = 1; i < s.size(); i++) {
        ans = max(ans, height[i]);
    }
    cout << ans << endl;
    return 0;
}

内容的提问来源于stack exchange,提问作者unglinh279

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 14:39:03