Gym102028K

题意

\(n\) 个点的 \(trie\) 和长度为 \(m\) 的串 \(s\)
多测,\(q\) 次询问,每次问 \(s[l...r]\)\(trie\) 匹配失败的次数以及最后的节点。

\(1\ \leq\ n,\ m,\ q\ \leq\ 10^5,\ 1\ \leq\ \sum n,\ \sum m,\ \sum q\ \leq\ 10^6\)

做法1

\(trie\) 每个点求出 \(hash\) 值,然后对串的每个 \(i\) 二分 \(s[i...m]\)\(trie\) 中的匹配长度。
时间复杂度 \(O(m\ log^2\ m)\)

做法2

\(trie\) 上每个节点可以匹配起点在 \(sa\) 中连续一段,长度为深度的串,对 \(trie\)\(dfs\),对每个节点记录 \([l,\ r]\) 表示 \(sa\) 中可以匹配的区间,然后二分即可。
时间复杂度 \(O(m\ log\ m)\)

代码

#include <bits/stdc++.h>

#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif

using namespace std;

int main() {
	ios::sync_with_stdio(false);
	auto solve = [&]() {
		static const int maxn = 1e5 + 10, maxm = (1 << 18) | 10, lgn = 17;
		static char s[maxn];
		static int sa[maxn], rnk[maxn], go[maxn][26], par[maxn][lgn], tpar[maxn][lgn], dep[maxn];
		static pair<int, int> seg[maxm], match[maxn];
		int n, m, q;
		scanf("%d%d%d", &n, &m, &q);
		for (int i = 0; i <= n; ++i) memset(go[i], 0, sizeof go[i]);
		for (int i = 1; i <= n; ++i) {
			int p;
			scanf("%d", &p);
			char c = getchar();
			while(c < 'a') c = getchar();
			go[p][c - 'a'] = i;
		}
		scanf("%s", s);
		auto SA = [&]() {
			static int pool[maxn], mx;
			static pair<pair<int, int>, int> p[maxn], q[maxn];
			memset(pool, 0, sizeof(pool[0]) * 256);
			for (int i = 0; i < m; ++i) pool[s[i]] = 1;
			for (int i = 1; i < 256; ++i) pool[i] += pool[i - 1];
			mx = pool[255];
			for (int i = 0; i < m; ++i) rnk[i] = pool[s[i]];
			for (int l = 1; mx < m; l <<= 1) {
				memset(pool, 0, sizeof(pool[0]) * (mx + 3));
				for (int i = 0; i < m; ++i) p[i] = make_pair(make_pair(rnk[i], (i + l < m ? rnk[i + l] : 0)), i), ++pool[p[i].first.second + 1];
				for (int i = 1; i < mx + 3; ++i) pool[i] += pool[i - 1];
				for (int i = 0; i < m; ++i) q[pool[p[i].first.second]++] = p[i];
				memset(pool, 0, sizeof(pool[0]) * (mx + 3));
				for (int i = 0; i < m; ++i) ++pool[q[i].first.first + 1];
				for (int i = 1; i < mx + 3; ++i) pool[i] += pool[i - 1];
				for (int i = 0; i < m; ++i) p[pool[q[i].first.first]++] = q[i];
				mx = 0;
				for (int i = 0; i < m; ++i) {
					if(!i || p[i].first != p[i - 1].first) ++mx;
					rnk[p[i].second] = mx;
				}
			}
			rnk[m] = 0;
			for (int i = 0; i <= m; ++i) sa[rnk[i]] = i;
			return;
		};
		SA();
		int N = 1;
		while(N <= m) N <<= 1;
		for (int i = 0; i < (N << 1); ++i) seg[i] = make_pair(0, 0);
		auto M = [&](int l, int r, pair<int, int> x) {
			for (l |= N, r |= N, --l, ++r; l ^ r ^ 1; l >>= 1, r >>= 1) {
				if(~l & 1) seg[l ^ 1] = max(seg[l ^ 1], x);
				if(r & 1) seg[r ^ 1] = max(seg[r ^ 1], x);
			}
			return;
		};
		s[m] = 0;
		function<void(int, int, int, int)> dfs = [&](int u, int d, int l, int r) {
			M(l, r, make_pair(d, u));
			for (int i = 0; i < 26; ++i) if(go[u][i]) {
				tpar[go[u][i]][0] = u;
				for (int j = 1; j < lgn; ++j) tpar[go[u][i]][j] = tpar[tpar[go[u][i]][j - 1]][j - 1];
				char foo = i + 'a';
				int lb = l, rb = r;
				while(lb <= rb) {
					int mid = lb + rb >> 1;
					if(s[sa[mid] + d] >= foo) rb = mid - 1;
					else lb = mid + 1;
				}
				if(lb <= r && s[sa[lb] + d] == foo) {
					int nl = lb;
					lb = l, rb = r;
					while(lb <= rb) {
						int mid = lb + rb >> 1;
						if(s[sa[mid] + d] > foo) rb = mid - 1;
						else lb = mid + 1;
					}
					dfs(go[u][i], d + 1, nl, rb);
				}
			}
			return;
		};
		dfs(0, 0, 1, m);
		for (int i = 0; i < N; ++i) {
			int j = i << 1;
			seg[j] = max(seg[j], seg[i]);
			j |= 1;
			seg[j] = max(seg[j], seg[i]);
		}
		for (int i = m - 1; ~i; --i) {
			match[i] = seg[rnk[i] | N];
			par[i][0] = i + match[i].first + 1;
			if(par[i][0] >= m) par[i][0] = i;
			dep[i] = (par[i][0] == i ? 0 : dep[par[i][0]] + 1);
			for (int j = 1; j < lgn; ++j) par[i][j] = par[par[i][j - 1]][j - 1];
		}
		auto tjump = [&](int u, int d) {
			for (int i = 0; i < lgn; ++i) if((d >> i) & 1) u = tpar[u][i];
			return u;
		};
#ifdef DEBUG
		for (int i = 0; i < m; ++i) cout << par[i][0] << ' '; cout << endl;
#endif
		while(q--) {
			int l, r;
			scanf("%d%d", &l, &r);
			--l;
			--r;
			int u = l;
			for (int i = lgn - 1; ~i; --i) if(par[u][i] <= r) u = par[u][i];
			auto t = match[u];
#ifdef DEBUG
			cout << u << ' ' << t.first << ' ' << t.second << endl;
#endif
			if(u + t.first == r) printf("%d 0\n", dep[l] - dep[u] + 1);
			else printf("%d %d\n", dep[l] - dep[u], tjump(t.second, t.first - (r - u + 1)));
		}
		return;
	};
	int T;
	scanf("%d", &T);
	while(T--) solve();
	return 0;
}
posted @ 2018-12-18 11:34  King_George  阅读(228)  评论(1)    收藏  举报