CF1037H. Security($sam$,線段樹)
阿新 • • 發佈:2020-12-22
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; }