【luogu U142356】勇者的后缀(SA)(主席树)(二分)

勇者的后缀

题目链接:luogu U142356

题目大意

给你一个字符串,每次询问给你 i,l,r 要你求所有 l~r 为起点的后缀中哪一个跟 i 为起点的后缀的最长公共前缀最长。
如果有多个一样长的,输出字典序最小的。

思路

首先考虑 SA / SAM。
然后如果是 SA,那两个后缀之间的最长公共前缀就是 rnk 区间中的 min
如果是 SAM,那就是 fail 树上的 LCA(反着建)

然后考虑怎么找 min 中最大的或者 LCA 最下面的。(其实会发现大概是同一种)
那因为是 min,自然是短的时候才会出现(LCA 其实最下面的也是要在 dfs 序最近的)
所以我们就找到它的前驱后继。

然而我们只能找 lr 的,所以我们可以用主席树来维护找。
(然后找前驱后继有一个简单的方法是先找它前面一共有多少个 k,然后前驱就是定位到第 k1 个,同理后继就是 k
(什么为啥总是大家都知道的我不知道,流眼泪的了)

然后发现如果答案相同要选字典序最小的。
考虑怎么搞,首先两个答案比一比,如果右边比左边优那右边肯定是右边最优的(因为都是按字典序排的,SAM 的 dfs 序其实也是字典序)

那左边的呢?
那我们就二分找到最左边的,然后就用那个即可。

代码

#include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 2e5 + 100; int n, ans1[N], ans2[N], ss[N]; char s[N]; struct Ask { int id, l, r; }; vector <Ask> q[N]; int sa[N], xx[N], yy[N], fir[N], log2_[N]; int height[N][21], rnk[N], tong[N], kind, ynum; void Sort(int m, int *x, int *y) { for (int i = 1; i <= m; i++) tong[i] = 0; for (int i = 1; i <= n; i++) tong[x[i]]++; for (int i = 2; i <= m; i++) tong[i] += tong[i - 1]; for (int i = n; i >= 1; i--) sa[tong[fir[i]]--] = y[i]; } void SA(int *r, int *sa, int n, int m) { int *x = xx, *y = yy; for (int i = 1; i <= n; i++) x[i] = r[i] + 1; for (int i = 1; i <= n; i++) y[i] = i; for (int i = 1; i <= n; i++) fir[i] = x[y[i]]; Sort(m, x, y); for (int j = 1; j < n; j <<= 1) { ynum = 0; for (int i = n - j + 1; i <= n; i++) y[++ynum] = i; for (int i = 1; i <= n; i++) if (sa[i] > j) y[++ynum] = sa[i] - j; for (int i = 1; i <= n; i++) fir[i] = x[y[i]]; Sort(m, x, y); swap(x, y); kind = 1; x[sa[1]] = 1; for (int i = 2; i <= n; i++) if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = kind; else x[sa[i]] = ++kind; if (kind == n) break; m = kind; } } void get_height(int *r, int *sa, int n) { int k = 0, j; for (int i = 1; i <= n; i++) rnk[sa[i]] = i; for (int i = 1; i <= n; i++) { if (k) k--; j = sa[rnk[i] - 1]; while (r[i + k] == r[j + k] && i + k <= n && j + k <= n) k++; height[rnk[i]][0] = k; } } void get_RMQ(int n) { log2_[0] = -1; for (int i = 1; i <= n; i++) log2_[i] = log2_[i >> 1] + 1; for (int i = 1; i <= 20; i++) for (int j = 1; j + (1 << i) - 1 <= n; j++) height[j][i] = min(height[j][i - 1], height[j + (1 << (i - 1))][i - 1]); } int RMQ_sa(int l, int r) { if (!l || !r) return 0; if (l == r) return n - l + 1; l = rnk[l]; r = rnk[r]; if (l > r) swap(l, r); l++; int k = log2_[r - l + 1]; return min(height[l][k], height[r - (1 << k) + 1][k]); } int RMQ_rnk(int l, int r) { if (!l || !r) return 0; if (l == r) return n - sa[l] + 1; if (l > r) swap(l, r); l++; int k = log2_[r - l + 1]; return min(height[l][k], height[r - (1 << k) + 1][k]); } struct XD_tree { int ls[N << 6], rs[N << 6], sum[N << 6], tot; int copy(int x) { int now = ++tot; ls[now] = ls[x]; rs[now] = rs[x]; sum[now] = sum[x]; return now; } void insert(int &now, int l, int r, int pl) { now = copy(now); sum[now]++; if (l == r) return ; int mid = (l + r) >> 1; if (pl <= mid) insert(ls[now], l, mid, pl); else insert(rs[now], mid + 1, r, pl); } int get_sum(int now1, int now2, int l, int r, int L, int R) { if (!(sum[now2] - sum[now1])) return 0; if (L <= l && r <= R) return sum[now2] - sum[now1]; int mid = (l + r) >> 1, re = 0; if (L <= mid) re += get_sum(ls[now1], ls[now2], l, mid, L, R); if (mid < R) re += get_sum(rs[now1], rs[now2], mid + 1, r, L, R); return re; } int get_k(int now1, int now2, int l, int r, int k) { if (l == r) return l; int mid = (l + r) >> 1; if (k <= sum[ls[now2]] - sum[ls[now1]]) return get_k(ls[now1], ls[now2], l, mid, k); else return get_k(rs[now1], rs[now2], mid + 1, r, k - (sum[ls[now2]] - sum[ls[now1]])); } }T; int rt[N]; int main() { scanf("%s", s + 1); n = strlen(s + 1); for (int i = 1; i <= n; i++) ss[i] = s[i] - 'a'; SA(ss, sa, n, n); get_height(ss, sa, n); get_RMQ(n); for (int i = 1; i <= n; i++) rt[i] = rt[i - 1], T.insert(rt[i], 1, n, rnk[i]); int Q; scanf("%d", &Q); for (int i = 1; i <= Q; i++) { int x, l, r; scanf("%d %d %d", &x, &l, &r); q[x].push_back((Ask){i, l, r}); } for (int i = 1; i <= n; i++) { int x = rnk[i]; for (int j = 0; j < q[i].size(); j++) { int id = q[i][j].id, l = q[i][j].l, r = q[i][j].r; int num = T.get_sum(rt[l - 1], rt[r], 1, n, 1, x - 1), L = 0, R = 0; if (num) L = T.get_k(rt[l - 1], rt[r], 1, n, num); if (num < T.sum[rt[r]] - T.sum[rt[l - 1]]) R = T.get_k(rt[l - 1], rt[r], 1, n, num + 1); int re1 = RMQ_rnk(L, x), re2 = RMQ_rnk(x, R); if (re2 > re1) { ans1[id] = re2; ans2[id] = sa[R]; } else { int L_ = 1, R_ = L, re = L; while (L_ <= R_) { int mid = (L_ + R_) >> 1; if (RMQ_rnk(mid, x) >= re1) re = mid, R_ = mid - 1; else L_ = mid + 1; } int numb = T.get_sum(rt[l - 1], rt[r], 1, n, 1, re - 1); L = T.get_k(rt[l - 1], rt[r], 1, n, numb + 1); ans1[id] = re1; ans2[id] = sa[L]; } } } for (int i = 1; i <= Q; i++) printf("%d %d\n", ans1[i], ans2[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_U142356.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示