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