CF1037H
题意
给长度为 \(n\) 字符串 \(s\)。
\(q\) 次询问,给 \(l,\ r,\ t\),问 \(s_l,\ ...,\ s_r\) 中比 \(t\) 字典序大的字典序最小的串。
\(n\ \leq\ 10^5,\ q\ \leq\ 2\ *\ 10^5,\ \sum\ t\ \leq\ 2\ *\ 10^5\)
做法1
建出 \(SA\) 后考虑如果知道了答案串和 \(t\) 的 lcp 的话,就变成了在 \(i\) 下标在 \([a,\ b]\), \(rnk\) 下标在 \([c,\ d]\) 中的 \(rnk\) 最小的串。二维数点 \(O(n\ log^2\ n)\),考虑优化。
发现是要进行若干次询问为下图的矩形的 \(rnk\) 的最小值。
i ^
|
| ---
| ----| |
| ----| | |
| | | | |
| | | | |
| | | | |
| ---------------
---------------------->
rnk
只用求出 \(i\) 下标在 \([l,\ r\ -\ lcp(t,\ sa[rnk(t)\ +\ 1])]\),\(rnk\ >\ rnk(t)\) 的 \(rnk\) 最小的串,\(i\) 下标 \(>\ max(l\ -\ 1,\ r\ -\ lcp(t,\ sa[rnk(t)\ +\ 1]))\) 暴力即可。
即将上图中纵向分割的图形横向分割。
按 \(rnk\) 排序后扫描线,用线段树维护第一次询问即可。
时间复杂度 \(O(n\ log\ n)\)
代码
#include <bits/stdc++.h>
#ifdef DEBUG
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif
#ifdef __WIN32
#define LLFORMAT "I64"
#define Rand() ((rand() << 15) + rand())
#else
#define LLFORMAT "ll"
#define Rand() (rand())
#endif
using namespace std;
const int maxn = 5e5 + 10, lgn = 19, N = 1 << 19;
int n, q, beg[maxn], l[maxn], r[maxn], ans[maxn][2], sa[maxn], h[maxn][lgn], rnk[maxn], len[maxn], ord[maxn], seg[(N << 1) | 10], LOG[maxn];
char s[maxn], t[maxn];
void SA() {
static int freq[maxn], mx;
static pair<pair<int, int>, int> p[maxn], q[maxn];
memset(freq, 0, sizeof freq);
for (int i = 1; i <= n; ++i) freq[s[i]] = 1;
for (int i = 1; i < maxn; ++i) freq[i] += freq[i - 1];
mx = freq[maxn - 1];
for (int i = 1; i <= n; ++i) rnk[i] = freq[s[i]];
for (int l = 1; mx < n; l <<= 1) {
memset(freq, 0, sizeof freq);
for (int i = 1; i <= n; ++i) p[i] = make_pair(make_pair(rnk[i], (i + l <= n ? rnk[i + l] : 0)), i), ++freq[p[i].first.second + 1];
for (int i = 1; i < maxn; ++i) freq[i] += freq[i - 1];
for (int i = 1; i <= n; ++i) q[++freq[p[i].first.second]] = p[i];
memset(freq, 0, sizeof freq);
for (int i = 1; i <= n; ++i) ++freq[q[i].first.first + 1];
for (int i = 1; i < maxn; ++i) freq[i] += freq[i - 1];
for (int i = 1; i <= n; ++i) p[++freq[q[i].first.first]] = q[i];
mx = 0;
for (int i = 1; i <= n; ++i) {
if(i == 1 || p[i].first != p[i - 1].first) ++mx;
rnk[p[i].second] = mx;
}
}
rnk[n + 1] = 0;
for (int i = 1; i <= n + 1; ++i) sa[rnk[i]] = i;
for (int i = 1, k = 0; i <= n; ++i) {
int j = sa[rnk[i] - 1];
if(k) --k;
while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) ++k;
h[rnk[i]][0] = k;
}
for (int lg = 1; lg < lgn; ++lg) {
int l = 1 << lg - 1;
for (int i = 1; i + l <= n; ++i) h[i][lg] = min(h[i][lg - 1], h[i + l][lg - 1]);
}
return;
}
void M(int i, int x) { for (i |= N; i; i >>= 1) seg[i] = min(seg[i], x); return; }
int Q(int s, int t) {
int x = maxn;
for (s |= N, t |= N, --s, ++t; s ^ t ^ 1; s >>= 1, t >>= 1) {
if(~s & 1) x = min(x, seg[s ^ 1]);
if(t & 1) x = min(x, seg[t ^ 1]);
}
return x;
}
int hQ(int l, int r) {
if(l == r) return h[l][0];
int lg = LOG[r - l + 1];
return min(h[l][lg], h[r - (1 << lg) + 1][lg]);
}
int lcp(int i, int j) {
i = rnk[i];
j = rnk[j];
if(i < j) return hQ(i + 1, j);
else return hQ(j + 1, i);
}
int main() {
for (int i = 2; i < maxn; ++i) LOG[i] = LOG[i >> 1] + 1;
for (int i = 1; i < ((N << 1) | 10); ++i) seg[i] = maxn;
scanf("%s", s + 1); n = strlen(s + 1); s[++n] = '$';
scanf("%d", &q);
for (int i = 1; i <= q; ++i) {
scanf("%d%d", l + i, r + i);
scanf("%s", t);
beg[i] = n + 1;
int &j = len[i];
for (j = 0; t[j]; ++j) s[++n] = t[j];
s[++n] = '$';
ord[i] = i;
}
SA();
sort(ord + 1, ord + q + 1, [&](int i, int j) { return rnk[beg[i]] > rnk[beg[j]]; });
for (int ii = 1, lst = n + 1; ii <= q; ++ii) {
int i = ord[ii], p = rnk[beg[i]];
if(p == n) { ans[i][0] = -1; continue; }
while(lst > p) --lst, M(sa[lst], lst);
int len = min(h[p + 1][0], ::len[i]), j;
if(::r[i] - len >= ::l[i]) {
j = Q(::l[i], ::r[i] - len);
if(j <= n) {
ans[i][0] = sa[j];
ans[i][1] = lcp(ans[i][0], beg[i]) + 1;
}
}
for (int j = max(::r[i] - len + 1, ::l[i]); j <= ::r[i]; ++j) {
if(rnk[j] > p) {
int LCP = lcp(j, beg[i]);
if(j + LCP <= ::r[i]) {
if(!ans[i][0] || rnk[ans[i][0]] > rnk[j]) ans[i][0] = j, ans[i][1] = LCP + 1;
}
}
}
if(!ans[i][0]) ans[i][0] = -1;
}
for (int i = 1; i <= q; ++i) {
if(!~ans[i][0]) puts("-1");
else {
for (int j = 0; j < ans[i][1]; ++j) putchar(s[ans[i][0] + j]);
putchar('\n');
}
}
return 0;
}