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;
}