给你一个长度为 N 的字符串 T , T[l,r] 表示 T 中第 l 个字符到第 r 个字符组成的子串,现在给你两种询问s’j’d’f’ha’s’k’l’j’d’f
1. 给你两个整数 k1 , k2 ,询问在所有 T 不相等的字符串中,字典序从小到大排序,排在第 k1 位的字符串 T[l,r] ,如果该子串出现了多次,则询问起始位置第 k2 小的那个。输出询问的那个子串的起始位置和终止位置(即 l 和 r )
2. 给定两个整数 l,r ,询问子串 T[l,r] 在 T 中不相同子串字典序排名 k1 ,以及在相同子串中从小到大的起始位置排名 k2
现在有 M 组询问,每组询问有三个正整数 type,x,y ,如果 type=1 则为第一种询问,否则为第二种询问,保证数据合法。
N≤500000 , M≤100000
首先我们考虑第一种询问。找出排名为 k1 的子串是 SA 的基本功能,这里就简要介绍一下。因为求的是不相同子串的字典序,所以按后缀排名后 Len−SAi+1−Heighti 就是每个后缀增加的子串个数,且字典序递增。我们可以用前缀和 Sumi 表示到拍完名的第 i 个后缀总共产生的字符串,我们只需二分一下就可以找出排名为第 k1 的字符串。
然后我们要考虑的就是相同子串中起始位置排名 k2 小的子串。由于拍完序后表示一段相同的子串的后缀肯定是连续一段,所以我们可以用二分确定能表示这个子串的区间。然后我们要做的就是在这个区间查询起始位置第 k2 的子串。这个是不是很眼熟,这就是区间第 k 小问题,只需用个主席树就可以解决了。到此第一种情况就解决了。
那么到了第二种情况,其实也差不多,只不过变成了给我们一个子串 T[l,r] ,设其长度为 len=r−l+1 。我们可以从 Rankl 中得知这个后缀在排序后的 SA 中的位置。根第一种询问相同,我们同样也可以先确定出能表示出这个子串的区间,假设这个区间的第一个数在 SA 中的位置是 p ,那么 Sump−1+len+Height[p] 就是我们要求的 k1 。为什么呢,显然的是 Heightp 的值必定是比 len 要小的,不然 p−1 就也应该在我们寻找的区间中,就矛盾了,得知这个我们就可以根据上面每个后缀增加子串的公式得到排名。同样 k2 为在这段区间中起始位置比 Rankl 前的相同子串用多少个,也用主席树就可以得到答案,那么这个第二种询问也解决了。
由于把主席树和后缀数组强行搞在了一起,所以有点小长。
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 5e5 + 5;
struct Tree {int l, r, Cnt;} Tr[MAXN * 20];
int Len, M, SA[MAXN], rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN];
int tot, N, Log[MAXN], Root[MAXN], Rmq[MAXN][21];
LL Sum[MAXN];
char S[MAXN];
void RSort() {
for (int i = 0; i <= M; i ++) tax[i] = 0;
for (int i = 1; i <= Len; i ++) tax[rank[tp[i]]] ++;
for (int i = 1; i <= M; i ++) tax[i] += tax[i - 1];
for (int i = Len; i >= 1; i --) SA[tax[rank[tp[i]]] --] = tp[i];
}
bool cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w];}
void Suffix() {
Len = strlen(S + 1);
for (int i = 1; i <= Len; i ++) rank[i] = S[i], tp[i] = i;
M = 127, RSort();
for (int i, p = 1, w = 1; p < Len; w += w, M = p) {
for (p = 0, i = Len - w + 1; i <= Len; i ++) tp[++ p] = i;
for (i = 1; i <= Len; i ++) if (SA[i] > w) tp[++ p] = SA[i] - w;
RSort(), swap(rank, tp), p = rank[SA[1]] = 1;
for (i = 2; i <= Len; i ++) rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
}
int j, k = 0;
for (int i = 1; i <= Len; Height[rank[i ++]] = k)
for (k = k ? k - 1 : k, j = SA[rank[i] - 1]; S[i + k] == S[j + k]; k ++);
}
void Insert(int &Now, int l, int r, int Rt, int Side) {
Now = ++ tot;
Tr[Now] = Tr[Rt];
Tr[Now].Cnt ++;
if (l == r) return;
int Mid = (l + r) >> 1;
if (Side <= Mid) Insert(Tr[Now].l, l, Mid, Tr[Rt].l, Side); else
Insert(Tr[Now].r, Mid + 1, r, Tr[Rt].r, Side);
}
int Query(int l, int r, int Rank, int Rl, int Rr) {
if (l == r) return l;
int Mid = (l + r) >> 1;
int ll = Tr[Rl].l, lr = Tr[Rl].r, rl = Tr[Rr].l, rr = Tr[Rr].r;
if (Tr[rl].Cnt - Tr[ll].Cnt >= Rank) return Query(l, Mid, Rank, ll, rl); else
return Query(Mid + 1, r, Rank - Tr[rl].Cnt + Tr[ll].Cnt, lr, rr);
}
int GetNum(int l, int r, int lx, int rx, int Rl, int Rr) {
if (rx < l || lx > r) return 0;
if (lx <= l && rx >= r) return Tr[Rr].Cnt - Tr[Rl].Cnt;
int Mid = (l + r) >> 1;
return GetNum(l, Mid, lx, rx, Tr[Rl].l, Tr[Rr].l) + GetNum(Mid + 1, r, lx, rx, Tr[Rl].r, Tr[Rr].r);
}
int Min(int l, int r) {
int Len = Log[r - l + 1];
return min(Rmq[l][Len], Rmq[r - (1 << Len) + 1][Len]);
}
void Prepare() {
for (int i = 1; i <= Len; i ++) Insert(Root[i], 1, Len, Root[i - 1], SA[i]);
for (int i = 1, j = 0; i <= Len; i <<= 1, j ++) Log[i] = j;
for (int i = 2; i <= Len; i++) Log[i] = max(Log[i - 1], Log[i]);
for (int i = 1; i <= Len; i ++) Rmq[i][0] = Height[i];
for (int j = 1; j <= 20; j ++)
for (int i = 1; i <= Len - (1 << (j - 1)) + 1; i ++)
Rmq[i][j] = min(Rmq[i][j - 1], Rmq[i + (1 << (j - 1))][j - 1]);
for (int i = 1; i <= Len; i ++) Sum[i] = Sum[i - 1] + Len - SA[i] + 1 - Height[i];
}
int GetL(int Now, int len) {
int l = 1, r = Now - 1, Ans = Now;
while (l <= r) {
int Mid = (l + r) >> 1;
if (Min(Mid + 1, Now) >= len) Ans = Mid, r = Mid - 1; else l = Mid + 1;
}
return Ans;
}
int GetR(int Now, int len) {
int l = Now + 1, r = Len, Ans = Now;
while (l <= r) {
int Mid = (l + r) >> 1;
if (Min(Now + 1, Mid) >= len) Ans = Mid, l = Mid + 1; else r = Mid - 1;
}
return Ans;
}
void Solve1(LL k1, LL k2) {
int l = 1, r = Len, Ans = 0;
while (l <= r) {
int Mid = (l + r) >> 1;
if (Sum[Mid] < k1) Ans = Mid, l = Mid + 1; else r = Mid - 1;
}
int len = k1 - Sum[Ans] + Height[++ Ans];
l = GetL(Ans, len), r = GetR(Ans, len);
if (l == r) {
printf("%d %d\n", SA[Ans], SA[Ans] + len - 1);
return;
}
Ans = Query(1, Len, k2, Root[l - 1], Root[r]);
printf("%d %d\n", Ans, Ans + len - 1);
}
void Solve2(LL l, LL r) {
int len = r - l + 1, start = l - 1, Rank = rank[l];
r = GetR(Rank, len), l = GetL(Rank, len);
LL k1 = Sum[l - 1] + len - Height[l];
printf("%lld %d\n", k1, GetNum(1, Len, 1, start, Root[l - 1], Root[r]) + 1);
}
int main() {
freopen("4150.in", "r", stdin), freopen("4150.out", "w", stdout);
scanf("%s", S + 1);
Suffix();
Prepare();
scanf("%d", &N);
for (int i = 1; i <= N; i ++) {
int Ord; LL l, r;
scanf("%d%lld%lld", &Ord, &l, &r);
if (Ord == 1) Solve1(l, r); else Solve2(l, r);
}
}