题意
给定一个长度为 \(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\n", suffixArray.query(l, r, k)); } } return 0; }