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;
}
posted @ 2018-09-03 22:18  King_George  阅读(336)  评论(0编辑  收藏  举报