1. 程式人生 > 實用技巧 >CF1037H. Security($sam$,線段樹)

CF1037H. Security($sam$,線段樹)

CF1037H. Security

\(Description\)

給定字串 \(S\),有 \(m\) 組詢問,每組詢問有 \(l, \ r, \ T\),輸出 \(S[l..r]\) 中字典序最小且嚴格大於 \(T\) 的子串。

\(Data \ Constraint\)

\(|S|, \ m, \ \sum{|T|} \leq 2 \times 10^5\)

考點

\(sam\),線段樹

\(Solution\)

考慮滿足條件的子串大概是什麼樣子的,不難想到可以表示為 \(A + c\)\(A\)\(T\) 的字首。

由於要求字典序最小,所以顯然 \(c\) 後不需要再新增任何字元,並且 \(|A|\)

最大。

所以我們對於原串 \(S\) 建出 \(sam\),然後對於詢問串 \(T\),在 \(sam\) 上匹配,由於要求是區間 \([l, \ r]\) 的子串,所以對於 \(sam\) 上的每個狀態,求出其 \(endpos\) 集合,設當前 \(T\) 匹配到第 \(k\) 位,那麼 \(endpos\) 需包含 \([l + k - 1, \ r]\) 中至少一個,線段樹合併可以簡單維護。

按匹配順序倒著求答案(\(|A|\) 最大),列舉 \(c\) 判斷即可(注意通過 \(c\) 轉移到的狀態的 \(endpos\) 也需要滿足條件),時間複雜度 \(O(26 n \log{n})\)

\(Code\)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define N 200000

#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)

void read(int &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
}


char ch[N + 1];

int n, m, rt[N + 1];

struct Tree {
    int sz[N << 5], lson[N << 5], rson[N << 5], tot;
    Tree() { tot = 0; }
    void Add(int &t, int l, int r, int k) {
        if (! t) t = ++ tot;
        ++ sz[t];
        if (l == r) return;
        int mid = l + r >> 1;
        k <= mid ? Add(lson[t], l, mid, k) : Add(rson[t], mid + 1, r, k);
    }
    int Merge(int u, int v, int l, int r) {
        if (! u || ! v) return u | v;
        if (l == r) return sz[++ tot] = sz[u] + sz[v], tot;
        int mid = l + r >> 1, cur = ++ tot;
        sz[cur] = sz[u] + sz[v];
        lson[cur] = Merge(lson[u], lson[v], l, mid);
        rson[cur] = Merge(rson[u], rson[v], mid + 1, r);
        return cur;
    }
    int Find(int t, int l, int r, int x, int y) {
        if (! t) return 0;
        if (l == r) return l;
        int mid = l + r >> 1;
        if (x <= l && r <= y)
            return sz[lson[t]] ? Find(lson[t], l, mid, x, y) : Find(rson[t], mid + 1, r, x, y);
        int d = 0;
        if (x <= mid && (d = Find(lson[t], l, mid, x, y)))
            return d;
        if (y > mid && (d = Find(rson[t], mid + 1, r, x, y)))
            return d;
        return 0;
    }
} tr;

int ans1, ans2, Len, L, R;

struct SAM {
    struct State { int next[26], len, link; };
    State st[N << 1];
    int sz, last;

    SAM() { sz = last = 0, st[0].len = 0, st[0].link = -1; }

    void Extend(char c, int endpos) {
        int cur = ++ sz, p = last, c0 = c - 'a';
        st[cur].len = st[p].len + 1;
        tr.Add(rt[cur], 1, n, endpos);
        while (p >= 0 && ! st[p].next[c0]) st[p].next[c0] = cur, p = st[p].link;
        if (p < 0)
            st[cur].link = 0;
        else {
            int q = st[p].next[c0];
            if (st[p].len + 1 == st[q].len)
                st[cur].link = q;
            else {
                int clone = ++ sz;
                st[clone] = st[q], st[clone].len = st[p].len + 1;
                while (p >= 0 && st[p].next[c0] == q)
                    st[p].next[c0] = clone, p = st[p].link;
                st[cur].link = st[q].link = clone;
            }
        }
        last = cur;
    }
    int d[N << 1], sta[N << 1];
    void Build_ep() {
        fo(i, 1, sz) ++ d[st[i].link];
        int t = 0;
        fo(i, 1, sz) if (! d[i])
            sta[ ++ t ] = i;
        for (int h = 0, x = 0; h ++ < t; ) {
            if (! -- d[st[x = sta[h]].link])
                sta[ ++ t ] = st[x].link;
            rt[st[x].link] = tr.Merge(rt[st[x].link], rt[x], 1, n);
        }
    }
    bool Find(int u, int c0, int k) {
        if (c0 > 25) return 0;
        int v = 0;
        fo(i, c0, 25) if (st[u].next[i]) {
            v = st[u].next[i];
            if (tr.Find(rt[v], 1, n, L + k, R))
                return ans1 = k, ans2 = i, 1;
        }
        return 0;
    }
    bool Solve(int u, int k) {
        if (u && ! tr.Find(rt[u], 1, n, L + k - 1, R))
            return 0;
        if (k == Len)
            return Find(u, 0, k);
        if (k == R - L)
            return Find(u, ch[k + 1] - 'a' + 1, k);
        int v = st[u].next[ch[k + 1] - 'a'];
        if (v && Solve(v, k + 1))
            return 1;
        return Find(u, ch[k + 1] - 'a' + 1, k);
    }
} sam;

int main() {
    freopen("security.in", "r", stdin);
    freopen("security.out", "w", stdout);

    scanf("%s\n", ch + 1); n = strlen(ch + 1);
    read(m);

    fo(i, 1, n)
        sam.Extend(ch[i], i);
    sam.Build_ep();

    fo(Case, 1, m) {
        read(L), read(R), scanf("%s\n", ch + 1);
        ans1 = ans2 = 0, Len = strlen(ch + 1);
        if (! sam.Solve(0, 0))
            puts("-1");
        else {
            fo(i, 1, ans1) putchar(ch[i]);
            putchar((char) ans2 + 'a'); puts("");
        }
    }

    return 0;
}