The meaning of problems
Given a string of length n, Q interrogation times, each query \ ((L, R & lt, K) \) , the answer substring \ (s_ls_ {l + 1} \ cdots s_r \) of \ (K \) occurrences of the position, if there outputs -1. \ (n \ le 1e5, Q \ le 1e5 \)
analysis
Discover the k-th position of the substring appears, it is easy to think of a powerful tool to use string processing - suffix array.
So how to use it? Each row of a sequence we first suffix string sample, then the sample is simulated
原 串: aaabaabaaaab
Ranking | suffix | position |
---|---|---|
1 | aaaab | 8 |
2 | AAAS | 9 |
3 | aaabaabaaab | 1 |
4 | aab | 10 |
5 | aabaaaab | 5 |
6 | aabaabaaab | 2 |
7 | from | 11 |
8 | abaaaab | 6 |
9 | Ababaab | 3 |
10 | b | 12 |
11 | baaaab | 7 |
12 | Babaab | 4 |
Query: [3,3], k = 4
[3,3] represents the substring \ (A \) , we can find the starting position for the suffix 3 \ (t = abaabaaab \) , the first character of the substring suffix represents the current to be queried, surprise found, while the substring has appeared in some other suffixes, the suffixes of these \ (T \) of the LCP (longest common prefix) is greater than or equal to 1. In this example we can find in the rankings LCP suffix t before 9 greater than 1, so only need to find the k-large at the beginning of the suffix. That is, [8,9,1,10,5,2,11,6,3]
to find the fourth largest, namely 5.
Query: [2,3], k = 2
[2,3] is a substring \ (AA \) , the start position of the suffix 2 \ (T = aabaabaaab \) , and \ (T \) the LCP is greater than the extension start position are equal to 2 [8,9,1,10,5,2]
, the second largest the location is 2.
So how is reflected in the program do?
Determined suffix array \ (rank, height \) array, using \ (ST \) table may be \ (O (1) \) query LCP two suffixes.
Further extension can be found in the ranking, the ranking is the suffix of the other suffix LCP x increases as the difference absolute value of rank decreases, it is possible to find a two-half interval ranking, so that all the suffixes within this range LCP and the target is greater than the length of the suffix is equal to substring queries.
After finding this interval, sustainable use of the tree line to find the k-th largest value (for sa arrays) to
Complexity Analysis: seek suffix array \ (O (nlog (n) ) \) , half \ (O (nlog (n) ) \) , Chairman of the tree query k-th largest value \ (O (nlog (n) ) \)
Overall complexity \ (O (nlog (n) ) \)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int MAXN = N;
char s[N];
int sa[N],x[N],y[N],c[N],rk[N],h[N],n,q;
int len, cnt;
int a[MAXN];
int b[MAXN];
int t[MAXN];
int ls[MAXN * 40];
int rs[MAXN * 40];
int sum[MAXN * 40];
int build(int l, int r) {
int rt = ++cnt;
int mid = l + r >> 1;
sum[rt] = 0;
if(l < r) {
ls[rt] = build(l, mid);
rs[rt] = build(mid + 1, r);
}
return rt;
}
int add(int o, int l, int r, int k) {
int rt = ++cnt;
int mid = l + r >> 1;
ls[rt] = ls[o]; rs[rt] = rs[o]; sum[rt] = sum[o] + 1;
if(l < r)
if(k <= mid) ls[rt] = add(ls[o], l, mid, k);
else rs[rt] = add(rs[o], mid + 1, r, k);
return rt;
}
int query(int ql, int qr, int l, int r, int k) {
int x = sum[ls[qr]] - sum[ls[ql]];
int mid = l + r >> 1;
if(l == r) return l;
if(x >= k) return query(ls[ql], ls[qr], l, mid, k);
else return query(rs[ql], rs[qr], mid + 1, r, k - x);
}
void build_sa(char *s,int n,int m){
memset(c,0,sizeof c);
for(int i=1;i<=n;++i) ++c[x[i] = s[i]];
for(int i=2;i<=m;++i) c[i] += c[i-1];
for(int i=n;i>=1;--i) sa[c[x[i]]--] = i;
for(int k=1;k<=n;k<<=1){
int p = 0;
for(int i=n-k+1;i<=n;++i) y[++p] = i;
for(int i=1;i<=n;++i) if(sa[i] > k) y[++p] = sa[i]-k;
for(int i=1;i<=m;++i) c[i] = 0;
for(int i=1;i<=n;++i) ++c[x[i]];
for(int i=2;i<=m;++i) c[i] += c[i-1];
for(int i=n;i>=1;--i) sa[c[x[y[i]]]--] = y[i] , y[i] = 0;
swap(x,y);
x[sa[1]] = 1; p = 1;
for(int i=1;i<=n;++i)
x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i] + k] == y[sa[i-1]+k] ? p : ++p);
if(p >= n)break;
m = p;
}
}
void get_height(){
int k = 0;
for(int i=1;i<=n;++i)rk[sa[i]] = i;
for(int i=1;i<=n;++i){
if(rk[i] == 1)continue;
if(k) --k;
int j = sa[rk[i]-1];
while(j + k <= n && i + k <= n && s[i+k] == s[j+k])++k;
h[rk[i]] = k;
}
}
int mm[N];
int best[20][N];
void initRMQ(int n){
mm[0] = -1;
for(int i=1;i<=n;i++)
mm[i] = ((i & (i-1)) == 0) ? mm[i-1] + 1 : mm[i-1];
for(int i=1;i<=n;i++)best[0][i] = i;
for(int i=1;i<=mm[n];i++)
for(int j=1;j+(1<<i)-1<=n;j++){
int a = best[i-1][j];
int b = best[i-1][j+(1<<(i-1))];
if(h[a] < h[b])best[i][j] = a;
else best[i][j] = b;
}
}
int askRMQ(int a,int b){
int t = mm[b-a+1];
b -= (1<<t) - 1;
a = best[t][a];b = best[t][b];
return h[a] < h[b] ? a : b;
}
int lcp(int a,int b){
if(a == b)return n;
if(a > b)swap(a,b);
return h[askRMQ(a+1,b)];
}
int getL(int l,int r,int len,int x){
while(l < r){
int mid = l + r >> 1;
if(lcp(mid,x) < len) l = mid + 1;
else r = mid;
}
return l;
}
int getR(int l,int r,int len,int x){
while(l < r){
int mid = (l + r + 1) >> 1;
if(lcp(mid,x) < len) r = mid - 1;
else l = mid;
}
return l;
}
int getAns(int l,int r,int k){
return query(t[l - 1], t[r], 1, n, k);
}
int solve(int l,int r,int k){
int len = r - l + 1;
int L = getL(1,rk[l],len,rk[l]);//二分找区间左端点
int R = getR(rk[l],n,len,rk[l]);//二分找区间右端点
if(k > R-L+1) return -1;
return getAns(L,R,k);//返回主席树查询结果
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&q);
scanf("%s",s+1);
build_sa(s,n,150);
get_height();
initRMQ(n);
//初始化主席树
cnt = 0;
t[0] = build(1,n);
for(int i=1;i<=n;i++){
int tt = sa[i];
t[i] = add(t[i-1],1,n,tt);
}
while(q --){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",solve(l,r,k));
}
}
return 0;
}