Hướng giải của Xâu con lặp dài nhất


Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.

Tác giả: kienadmin

Lời giải: Xâu con lặp lại dài nhất (SA)

Phân tích

Tính LCP giữa các hậu tố kề nhau. LCP[i] = độ dài tiền tố chung của SA[i]SA[i+1]. Nếu LCP[i] ≥ 1 thì có xâu con độ dài LCP[i] lặp lại ≥ 2 lần. Đáp số = max(LCP).

Độ phức tạp

  • Thời gian: O(N log N)
  • Không gian: O(N)

Code mẫu C++

#include <bits/stdc++.h>
using namespace std;
int main() {
    string s;
    cin >> s;
    int n = s.size();

    vector<int> sa(n), rank(n), tmp(n);
    for (int i = 0; i < n; i++) { sa[i] = i; rank[i] = s[i]; }
    for (int k = 1; k < n; k *= 2) {
        auto cmp = [&](int a, int b) {
            if (rank[a] != rank[b]) return rank[a] < rank[b];
            int ra = (a + k < n) ? rank[a + k] : -1;
            int rb = (b + k < n) ? rank[b + k] : -1;
            return ra < rb;
        };
        sort(sa.begin(), sa.end(), cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i < n; i++)
            tmp[sa[i]] = tmp[sa[i-1]] + cmp(sa[i-1], sa[i]);
        rank = tmp;
    }
    vector<int> lcp(n, 0);
    vector<int> inv(n);
    for (int i = 0; i < n; i++) inv[sa[i]] = i;
    int k = 0;
    for (int i = 0; i < n; i++) {
        if (inv[i] == n - 1) { k = 0; continue; }
        int j = sa[inv[i] + 1];
        while (i + k < n && j + k < n && s[i + k] == s[j + k]) k++;
        lcp[inv[i]] = k;
        if (k > 0) k--;
    }

    // Đếm xâu con xuất hiện >= 2 lần không overlap
    long long cnt = 0;
    int i = 0;
    while (i < n - 1) {
        if (lcp[i] > 0) {
            // Tìm đoạn liên tiếp có LCP >= 1
            int j = i;
            int min_lcp = lcp[i];
            while (j < n - 1 && lcp[j] > 0) {
                min_lcp = min(min_lcp, lcp[j]);
                j++;
            }
            // j-i hậu tố đầu tiên có hậu tố xuất hiện >= 2 lần
            // xâu con độ dài từ 1 đến min_lcp
            // Số xâu con phân biệt xuất hiện ít nhất 2 lần = min_lcp (vì đếm min chung)
            // nhưng cần chính xác: số xâu con độ dài L xuất hiện >= 2 lần nếu có >= 2 hậu tố có LCP >= L
            cnt += min_lcp;
            i = j;
        } else {
            i++;
        }
    }
    cout << cnt << endl;
    return 0;
}

Nhận xét

Không có ý kiến tại thời điểm này.