← 全部文章

CF666E Forensic Examination 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF666E Forensic Examination

题意

  • 给定一个字符串 ss,以及 mm 个字符串 t1…mt_{1 \dots m}。
  • qq 次询问,每次询问 s[pl:pr]s[p_l:p_r] 在字符串 tl…rt_{l\dots r} 中的哪个串里作为子串出现的次数最多,如果有多个答案,输出最小的一个。
  • ∣s∣,q≤5×105|s|,q \le 5 \times 10^5,m,∑i=1m∣ti∣≤5×104m,\sum_{i=1}^m |t_i| \le 5 \times 10^4。

题解

将 ss 和 t1…mt_{1\dots m} 合成一个字符串,中间用一个其它的分隔符隔开。

设这个新字符串为 s′s^\prime,有 ∣s′∣=∣s∣+∑i=1m∣ti∣+m|s^\prime| = |s| + \sum_{i=1}^{m}|t_i| + m。

对这个字符串求 SA,则一次询问 (pl,pr,l,r)(p_l,p_r,l,r) 的答案为:

SA 中 plp_l 所在的位置前后 height⁡\operatorname{height} 不小于 pr−pl+1p_r - p_l + 1 的区间中,每个后缀对应 tit_i 的 ii 在 [l,r][l,r] 中的众数。

如果我们按照 height⁡\operatorname{height} 由大到小合并,并建出 kruskal 重构树。

那一次询问就等价于查询一棵子树内颜色在 [l,r][l,r] 中的众数,倍增找到子树的根后,线段树合并即可。

总时间复杂度 O(∣s′∣log⁡∣s′∣+q(log⁡∣s′∣+log⁡m))\mathcal O(|s^\prime| \log |s^\prime| + q (\log |s^\prime| + \log m))。

代码

const int N = 6e5 + 7, M = 21;
int n, m, len[N], q;
char s[N], ss[N];
struct SA {
	int n, m = 127, sa[N], rk[N<<1], tp[N<<1], tx[N], he[N];
	char s[N];
	inline void sort() {
		for (int i = 1; i <= m; i++) tx[i] = 0;
		for (int i = 1; i <= n; i++) ++tx[rk[i]];
		for (int i = 1; i <= m; i++) tx[i] += tx[i-1];
		for (int i = n; i; i--) sa[tx[rk[tp[i]]]--] = tp[i];
	}
	inline bool pd(int i, int j, int k) {
		return tp[i] == tp[j] && tp[i+k] == tp[j+k];
	}
	inline void main() {
		for (int i = 1; i <= n; i++) rk[i] = s[i], tp[i] = i;
		sort();
		for (int k = 1, p; p < n; k <<= 1, m = p) {
			p = 0;
			for (int i = 1; i <= k; i++) tp[++p] = n - k + i;
			for (int i = 1; i <= n; i++)
				if (sa[i] > k) tp[++p] = sa[i] - k;
			sort(), swap(rk, tp), rk[sa[1]] = p = 1;
			for (int i = 2; i <= n; i++)
				rk[sa[i]] = p += !pd(sa[i], sa[i-1], k);
		}
	}
	inline void height() {
		for (int i = 1, p = 0; i <= n; i++) {
			if (p) --p;
			int j = sa[rk[i]-1];
			while (s[i+p] == s[j+p]) ++p;
			he[rk[i]] = p;
		}
	}
} sa;
struct T {
	int l, r;
	pi x;
} t[N<<6|1];
int p[N], rt[N<<1], tot, d[N<<1], num, f[N<<1][M], u[N<<1];

int get(int x) {
	return x == u[x] ? x : (u[x] = get(u[x]));
}

inline pi operator + (pi x, pi y) {
	return x.se < y.se ? y : x;
}

int ins(int l, int r, int x) {
	int p = ++tot;
	if (l == r) return t[p].x = mp(x, 1), p;
	int mid = (l + r) >> 1;
	if (x <= mid) t[p].l = ins(l, mid, x);
	else t[p].r = ins(mid + 1, r, x);
	t[p].x = t[t[p].l].x + t[t[p].r].x;
	return p;
}

int merge(int p, int q, int l, int r) {
	if (!p || !q) return p | q;
	int o = ++tot;
	if (l == r) return t[o].x = t[p].x, t[o].x.se += t[q].x.se, o;
	int mid = (l + r) >> 1;
	t[o].l = merge(t[p].l, t[q].l, l, mid);
	t[o].r = merge(t[p].r, t[q].r, mid + 1, r);
	t[o].x = t[t[o].l].x + t[t[o].r].x;
	return o;
}

pi ask(int p, int l, int r, int L, int R) {
	if (l >= L && r <= R) return t[p].x;
	int mid = (l + r) >> 1;
	pi o = mp(0, 0);
	if (L <= mid) o = ask(t[p].l, l, mid, L, R);
	if (R > mid) o = o + ask(t[p].r, mid + 1, r, L, R);
	return o;
}

int main() {
	rds(s, n), rd(m);
	for (int i = 1; i <= m; i++) {
		rds(ss, len[i]);
		for (int j = 1; j <= len[i]; j++) sa.s[++sa.n] = ss[j];
		sa.s[++sa.n] = sa.m, len[i] += len[i-1] + 1;
	}
	for (int i = 1; i <= n; i++) sa.s[++sa.n] = s[i];
	sa.main(), sa.height();
	for (int i = 1; i <= m; i++)
		for (int j = len[i-1] + 1; j < len[i]; j++)
			rt[sa.rk[j]] = ins(1, m, i);
	for (int i = 1; i <= sa.n; i++) p[i] = u[i] = i;
	sort(p + 2, p + sa.n + 1, [](int i, int j) {
		return sa.he[i] > sa.he[j];
	});
	num = sa.n;
	for (int i = 2; i <= sa.n; i++) {
		d[++num] = sa.he[p[i]];
		int x = get(p[i]), y = get(p[i] - 1);
		f[x][0] = f[y][0] = u[x] = u[y] = u[num] = num;
		rt[num] = merge(rt[x], rt[y], 1, m);
	}
	for (int j = 1; j < M; j++)
		for (int i = 1; i <= num; i++)
			f[i][j] = f[f[i][j-1]][j-1];
	rd(q);
	for (int i = 1, l, r, pl, pr; i <= q; i++) {
		rd(l), rd(r), rd(pl), rd(pr);
		int x = sa.rk[pl+len[m]], k = pr - pl + 1;
		for (int j = M - 1; ~j; j--)
			if (d[f[x][j]] >= k) x = f[x][j];
		pi ans = ask(rt[x], 1, m, l, r);
		print(ans.fi ? ans.fi : l, ' '), print(ans.se);
	}
	return 0;
}

标签:Kruskal 重构树、SA、并查集、线段树合并

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…