题意
给定一个长度为 (n) 的字符串,有 (q) 次询问,每次询问求字符串的 ([l, r]) 所形成的子串第 (k) 次出现时的位置。
思路
([l, r]) 形成的子串,它是一些后缀的前缀,并且这些前缀在后缀数组中的rk是相邻的,因此,对于([l, r]) 我们可以通过 rmq/lcp 求出他的区间公共前缀长度,之后二分长度大于等于 (r-l+1) 的左右端点,只需要求出这些端点的第 (k) 小就可以,对于这些端点,可以通过主席树维护 (sa[i]) ,之后就可以 (O(log)) 查询第K小了 。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
const int maxm = maxn*20;
int n;
char str[maxn];
struct node {
int l, r, val;
}tr[maxm];
namespace HJTree {
int tot = 0;
void init() { tot = 0; }
void build(int l, int r, int &x) {
x = ++tot;
tr[x].val = 0;
if(l==r) return;
int mid = l+r>>1;
build(l, mid, tr[x].l);
build(mid+1, r, tr[x].r);
}
void update(int l, int r, int x, int &y, int p) {
y = ++tot;
// cout << l << " " << r << " " << x << " " << y << " " << p << endl;
tr[y] = tr[x];
++tr[y].val;
if(l==r) return;
int mid = l+r >> 1;
if(p <= mid) update(l, mid, tr[x].l, tr[y].l, p);
else update(mid+1, r, tr[x].r, tr[y].r, p);
}
int query(int l, int r, int x, int y, int k) {
if(l==r) return l;
int mid = l+r>>1;
int tmp = tr[tr[y].l].val - tr[tr[x].l].val;
if(tmp >= k) return query(l, mid, tr[x].l, tr[y].l, k);
return query(mid+1, r, tr[x].r, tr[y].r, k-tmp);
}
}
struct SuffixArray {
int x[maxn], y[maxn], c[maxn];
int sa[maxn], rk[maxn], height[maxn];
void SA() {
int m = 26;
for (int i=0; i<=m; ++i) c[i] = 0;
for (int i=1; i<=n; ++i) ++c[(x[i] = str[i]-'a')];
for (int i=1; i<=m; ++i) c[i] += c[i-1];
for (int i=n; i>=1; --i) sa[c[x[i]]--] = i;
for (int p, k=1; k<=n; k<<=1) {
p = 0;
for (int i=n; i>n-k; --i) y[++p] = i;
for (int i=1; i<=n; ++i) {
if(sa[i]>k)
y[++p] = sa[i]-k;
}
for (int i=0; i<=m; ++i) c[i] = 0;
for (int i=1; i<=n; ++i) ++c[x[y[i]]];
for (int i=1; i<=m; ++i) c[i] += c[i-1];
for (int i=n; i>=1; --i) sa[c[x[y[i]]]--] = y[i];
p = y[sa[1]] = 1;
for (int a, b, i=2; i<=n; ++i) {
a = sa[i]+k>n? -1: x[sa[i]+k];
b = sa[i-1]+k>n? -1: x[sa[i-1]+k];
y[sa[i]] = (x[sa[i]]==x[sa[i-1]] && a==b)? p: ++p;
}
swap(x, y);
if(p>=n) break;
m = p;
}
for (int i=1; i<=n; ++i) rk[i] = x[i];
}
void getHeight() {
int k = 0;
for (int i=1; i<=n; ++i) {
if(k) --k;
int j = sa[rk[i]-1];
while(str[i+k] == str[j+k]) ++k;
height[rk[i]] = k;
}
}
int root[maxn];
int st[maxn][20];
void build() {
SA();
HJTree::init();
HJTree::build(1, n, root[0]);
for (int i=1; i<=n; ++i) HJTree::update(1, n, root[i-1], root[i], sa[i]);
}
void ST() {
getHeight();
// for (int i=1; i<=n; ++i) printf("%d ", sa[i]); puts("");
// for (int i=1; i<=n; ++i) printf("%d ", rk[i]); puts("");
// for (int i=1; i<=n; ++i) printf("%d ", height[i]); puts("");
for (int i=1; i<=n; ++i) st[i][0] = height[i];
for (int j=1; j<=20; ++j) {
int p = 1<<j-1, l = (1<<j)-1;
for (int i=1; i+l<=n; ++i)
st[i][j] = min(st[i][j-1], st[i+p][j-1]);
}
}
int rmq(int l, int r) {
if(l==r) return 0x3f3f3f3f;
++l;
int len = r-l+1, d=0;
while((1<<d+1)<=len) ++d;
int p = 1<<d;
return min(st[l][d], st[r-p+1][d]);
}
int findL(int pos, int len) {
int l = 1, r = pos;
while (l <= r) {
int mid = l + r >> 1;
if (rmq(mid, pos) < len) l=mid+1;
else r=mid-1;
}
return l;
}
int findR(int pos, int len) {
int l = pos, r = n;
while (l <= r) {
int mid = l + r >> 1;
if (rmq(pos, mid) < len) r=mid-1;
else l=mid+1;
}
return r;
}
int query(int l, int r, int k) {
int len = r-l+1;
int pos = rk[l];
// cout << pos << " " << len << " ";
int L = findL(pos, len);
int R = findR(pos, len);
// cout << L << " " << R << endl;
if(R-L+1<k) return -1;
return HJTree::query(1, n, root[L-1], root[R], k);
}
}suffixArray;
int T, q;
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &q);
scanf("%s", str+1);
suffixArray.build();
suffixArray.ST();
for (int l, r, k, i=1; i<=q; ++i) {
scanf("%d%d%d", &l, &r, &k);
printf("%d
", suffixArray.query(l, r, k));
}
}
return 0;
}