1 条题解

  • 0
    @ 2026-1-31 21:11:12

    预处理阶段

    1. 后缀自动机(SAM)构建:

      O(m)O(m)

      其中 mm 是模式串 TT 的长度。

    2. 文本串 SS 匹配:

      O(n)O(n)

      其中 nn 是文本串 SS 的长度。

    3. 稀疏表(ST表)预处理:

      O(nlog⁡n)O(n \log n)

      其中 nn 是文本串 SS 的长度。

    查询阶段

    1. 每组查询的二分时间复杂度:

      O(log⁡n)O(\log n)

      其中 nn 是文本串 SS 的长度。

    2. 单次区间最大值查询:

      O(1)O(1)

    整体时间复杂度

    O(m+nlog⁡n+qlog⁡n)O(m + n \log n + q \log n)

    其中 mm 是模式串 TT 的长度,nn 是文本串 SS 的长度,qq 是查询次数。

    关键验证逻辑

    对于二分答案中的长度 midmid,验证条件为: 存在 k∈[l+mid−1,r]k \in [l + mid - 1, r] 使得 lenS[k]≥midlen_S[k] \geq mid,其中 lenS[k]len_S[k] 表示 SS 中以第 kk 个字符结尾的子串与 TT 的最长公共子串长度。

    算法步骤

    1. 构建 TT 的 SAM:线性时间 O(m)O(m)
    2. 匹配 SS 得到 lenSlen_S 数组:线性时间 O(n)O(n)
    3. 预处理 lenSlen_S 的 ST 表:O(nlog⁡n)O(n \log n)
    4. 处理每组查询:
      • 二分答案:O(log⁡n)O(\log n)
      • 每次验证:O(1)O(1)(ST 表区间最大值查询)

    这种组合解法充分利用了各算法的优势,确保在大数据范围下的高效运行。

    AC code:

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 2e5 + 5;
    const int MAX_SAM = 4e5 + 5;
    
    struct SAMNode {
        int len, link;
        int next[2];
        SAMNode() { len = 0; link = -1; memset(next, -1, sizeof(next)); }
    } sam[MAX_SAM];
    int sam_cnt, last;
    
    void sam_init() {
        sam_cnt = 0;
        last = 0;
        sam[0] = SAMNode();
    }
    
    void sam_extend(int c) {
        int cur = ++sam_cnt;
        sam[cur].len = sam[last].len + 1;
        int p = last;
        while (p != -1 && sam[p].next[c] == -1) {
            sam[p].next[c] = cur;
            p = sam[p].link;
        }
        if (p == -1) {
            sam[cur].link = 0;
        } else {
            int q = sam[p].next[c];
            if (sam[p].len + 1 == sam[q].len) {
                sam[cur].link = q;
            } else {
                int clone = ++sam_cnt;
                sam[clone].len = sam[p].len + 1;
                memcpy(sam[clone].next, sam[q].next, sizeof(sam[q].next));
                sam[clone].link = sam[q].link;
                while (p != -1 && sam[p].next[c] == q) {
                    sam[p].next[c] = clone;
                    p = sam[p].link;
                }
                sam[q].link = clone;
                sam[cur].link = clone;
            }
        }
        last = cur;
    }
    
    int log_table[MAXN];
    int dp[MAXN][20];
    int len_S[MAXN];
    int n, m, q;
    void st_preprocess() {
        log_table[1] = 0;
        for (int i = 2; i <= n; i++) {
            log_table[i] = log_table[i / 2] + 1;
        }
        int k_max = log_table[n] + 1;
        for (int i = 1; i <= n; i++) {
            dp[i][0] = len_S[i];
        }
        for (int j = 1; j < k_max; j++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i++) {
                dp[i][j] = max(dp[i][j-1], dp[i + (1 << (j-1))][j-1]);
            }
        }
    }
    
    int st_query(int l, int r) {
        if (l > r) return 0;
        int k = log_table[r - l + 1];
        return max(dp[l][k], dp[r - (1 << k) + 1][k]);
    }
    
    int main() {
        freopen("seal.in", "r", stdin);
        freopen("seal.out", "w", stdout);
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        cin >> n >> m >> q;
        string S, T;
        cin >> S >> T;
        S = " " + S;
        T = " " + T;
        sam_init();
        for (int i = 1; i <= m; i++) {
            sam_extend(T[i] - '0');
        }
        int cur = 0, cnt = 0;
        for (int i = 1; i <= n; i++) {
            int c = S[i] - '0';
            while (cur != -1 && sam[cur].next[c] == -1) {
                cur = sam[cur].link;
                if (cur != -1) cnt = sam[cur].len;
                else cnt = 0;
            }
            if (cur == -1) {
                cur = 0;
                cnt = 0;
            } else {
                cur = sam[cur].next[c];
                cnt++;
            }
            len_S[i] = cnt;
        }
        st_preprocess();
        while (q--) {
            int l, r;
            cin >> l >> r;
            int max_L = min(r - l + 1, m);
            int low = 0, high = max_L, ans = 0;
            while (low <= high) {
                int mid = (low + high) / 2;
                if (mid == 0) {
                    ans = mid;
                    low = mid + 1;
                    continue;
                }
                int Lk = l + mid - 1;
                int Rk = r;
                if (Lk > Rk) {
                    high = mid - 1;
                    continue;
                }
                int max_val = st_query(Lk, Rk);
                if (max_val >= mid) {
                    ans = mid;
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            cout << ans << endl;
        }
    
        return 0;
    }
    
    • 1

    [BJOI 2020 Appendix] Simple Interval LCS Problem

    信息

    ID
    200
    时间
    1000ms
    内存
    512MiB
    难度
    8
    标签
    递交数
    6
    已通过
    3
    上传者