1 条题解

  • 0
    @ 2026-1-15 8:54:29

    出现至少 𝑘 次意味着后缀排序后有至少连续 𝑘 个后缀以这个子串作为公共前缀.

    所以,求出每相邻 𝑘 −1 个 ℎ𝑒𝑖𝑔ℎ𝑡 的最小值,再求这些最小值的最大值就是答案.

    可以使用单调队列 𝑂(𝑛) 解决,但使用其它方式也足以 AC.

    #include <cstring>
    #include <iostream>
    #include <set>
    
    using namespace std;
    
    constexpr int N = 40010;
    
    int n, k, a[N], sa[N], rk[N], oldrk[N], id[N], px[N], cnt[1000010], ht[N], ans;
    multiset<int> t;  // multiset 是最好写的实现方式
    
    bool cmp(int x, int y, int w) {
      return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
    }
    
    int main() {
      cin.tie(nullptr)->sync_with_stdio(false);
      int i, j, w, p, m = 1000000;
    
      cin >> n >> k;
      --k;
    
      for (i = 1; i <= n; ++i) cin >> a[i];  // 求后缀数组
      for (i = 1; i <= n; ++i) ++cnt[rk[i] = a[i]];
      for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];
      for (i = n; i >= 1; --i) sa[cnt[rk[i]]--] = i;
    
      for (w = 1; w < n; w <<= 1, m = p) {
        for (p = 0, i = n; i > n - w; --i) id[++p] = i;
        for (i = 1; i <= n; ++i)
          if (sa[i] > w) id[++p] = sa[i] - w;
        memset(cnt, 0, sizeof(cnt));
        for (i = 1; i <= n; ++i) ++cnt[px[i] = rk[id[i]]];
        for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];
        for (i = n; i >= 1; --i) sa[cnt[px[i]]--] = id[i];
        memcpy(oldrk, rk, sizeof(rk));
        for (p = 0, i = 1; i <= n; ++i)
          rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
      }
    
      for (i = 1, j = 0; i <= n; ++i) {  // 求 height
        if (j) --j;
        while (a[i + j] == a[sa[rk[i] - 1] + j]) ++j;
        ht[rk[i]] = j;
      }
    
      for (i = 1; i <= n; ++i) {  // 求所有最小值的最大值
        t.insert(ht[i]);
        if (i > k) t.erase(t.find(ht[i - k]));
        ans = max(ans, *t.begin());
      }
    
      cout << ans;
    
      return 0;
    }
    
    • 1

    信息

    ID
    1917
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    5
    已通过
    2
    上传者