题意:给出一个长度为n的字符串,q次询问,每次询问给出参数
l
,
r
,
k
l,r,k
l,r,k,询问整个字符串中第k个子串
s
l
s
l
+
1
.
.
.
.
s
r
s_ls_{l+1}....s_r
slsl+1....sr。
数据范围:
1
0
5
10^5
105
后缀数组的一些定义:
后缀字符串:以i开始一直到结尾的字符串。
- s a [ i ] sa[i] sa[i]:排名为i的后缀字符串。
- r a n k [ i ] rank[i] rank[i]:后缀字符串i的排名。
- h e i g h t [ i ] height[i] height[i]:排名为 i i i与 i − 1 i-1 i−1的最长公共前缀的长度。
首先预处理出以上数组,建立一个height数组的线段树,维护最大最小值,那么如果要查询字符串
[
l
,
r
]
[l,r]
[l,r],那么是不是所有可能的和他前缀相同的一定和他是排名连续的,设
p
=
r
a
n
k
[
l
]
p=rank[l]
p=rank[l],一定是排名p之前的连续的一段可能为答案,p之后的连续的一段可能为答案。
现在只考虑排名在他之前的,之后的同理。
那么考虑暴力做法,是不是依次查看
h
e
i
g
h
t
[
i
]
,
1
<
=
i
<
=
p
height[i],1<=i<=p
height[i],1<=i<=p是不是大于等于
r
−
l
+
1
r-l+1
r−l+1的,直到找到最近的一个不满足的x,是不是说明
[
x
,
p
]
[x,p]
[x,p]之间的都是满足题意要求的子串,那么同样的可以求出其后面的。这是暴力的做法,可以用线段树或者二分加st表优化到log。
然后如果我们知道了所有的满足条件的排名区间
[
r
a
n
k
L
,
r
a
n
R
]
[rankL,ranR]
[rankL,ranR],是不是说明一定有
r
a
n
k
R
−
r
a
n
L
+
1
rankR-ranL+1
rankR−ranL+1这么多个满足题意要求的子串,那么是不是只要想办法将
[
r
a
n
k
L
,
r
a
n
k
R
]
[rankL,rankR]
[rankL,rankR]内的这些后缀字符串的下标变的有序的话就可以知道第k小的字符串下标是谁了。这个可以将排名从小到大,sa数组作为值域区间,建立主席树,查询区间第k小即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
const int ALP=26;
int sa[maxn],Rank[maxn],rank2[maxn],height[maxn],cnt[maxn],*x,*y,mxx[maxn];
char s[maxn];
void radix_sort(int n,int m){
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;i++) cnt[x[y[i]]]++;
for(int i=1;i<m;i++) cnt[i]+=cnt[i-1];
for(int i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]]=y[i];
}
void get_sa(char s[],int n){
int m=128;
x=Rank,y=rank2;
for(int i=0;i<n;i++) x[i]=s[i],y[i]=i;
radix_sort(n,m);
for(int len=1;len<n;len<<=1){
int p=0;
for(int i=n-len;i<n;i++) y[p++]=i;
for(int i=0; i<n; i++) if(sa[i]>=len) y[p++]=sa[i]-len;
radix_sort(n,m);
swap(x,y);
x[sa[0]]=p=0;
for(int i=1;i<n;i++){
if(y[sa[i-1]]==y[sa[i]]&&sa[i-1]+len<n&&sa[i]+len<n&&y[sa[i-1]+len]==y[sa[i]+len])
x[sa[i]]=p;
else
x[sa[i]]=++p;
}
m=p+1;
if(m>=n) break;
}
for(int i=0;i<n;i++) Rank[i]=x[i];
}
void get_height(char s[],int n){
int k=0;
for(int i=0;i<n;i++){
if(Rank[i] == 0) continue;
k=max(0,k-1);
int j=sa[Rank[i]-1];
while(i+k<n&&j+k<n&&s[i+k]==s[j+k]) k++;
height[Rank[i]] = k;
}
}
int maxx[maxn<<2|1],minn[maxn<<2|1];
int n;
void build(int l,int r,int k){
if(l==r){
maxx[k]=minn[k]=height[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
maxx[k]=max(maxx[k<<1],maxx[k<<1|1]);
minn[k]=min(minn[k<<1],minn[k<<1|1]);
}
int myfindL(int l,int r,int k,int p,int val){
if(p<1) return -1;
if(l==r){
if(maxx[k]<val) return l;
return -1;
}
if(minn[k]>=val) return -1;
int mid=(l+r)>>1;
int res=-1;
if(p<=mid) return myfindL(l,mid,k<<1,p,val);
if(maxx[k<<1|1]<val) return myfindL(mid+1,r,k<<1|1,p,val);
res=myfindL(mid+1,r,k<<1|1,p,val);
if(res!=-1) return res;
return myfindL(l,mid,k<<1,p,val);
}
int myfindR(int l,int r,int k,int p,int val){
if(p>n) return -1;
if(l==r){
if(maxx[k]<val) return l;
return -1;
}
if(minn[k]>=val) return -1;
int mid=(l+r)>>1;
if(p>mid) return myfindR(mid+1,r,k<<1|1,p,val);
int res=-1;
if(maxx[k<<1]<val) return myfindR(l,mid,k<<1,p,val);
res=myfindR(l,mid,k<<1,p,val);
if(res!=-1) return res;
return myfindR(mid+1,r,k<<1|1,p,val);
}
struct Tree{
int lc,rc,sum;
}tree[maxn*30];
int root[maxn];
int tot;
int build(int l,int r){
int k=++tot;
tree[k].sum=0;
if(l==r) return k;
int mid=(l+r)>>1;
tree[k].lc=build(l,mid);
tree[k].rc=build(mid+1,r);
return k;
}
int updata(int p,int l,int r,int id,int val){
int k=++tot;
tree[k]=tree[p];
++tree[k].sum;
if(l==r) return k;
int mid=(l+r)>>1;
if(id<=mid) tree[k].lc=updata(tree[p].lc,l,mid,id,val);
else tree[k].rc=updata(tree[p].rc,mid+1,r,id,val);
return k;
}
int myfindk(int p,int q,int l,int r,int k){
if(l==r) return l;
int mid=(l+r)>>1;
int val=tree[tree[p].lc].sum-tree[tree[q].lc].sum;
if(val>=k) return myfindk(tree[p].lc,tree[q].lc,l,mid,k);
return myfindk(tree[p].rc,tree[q].rc,mid+1,r,k-val);
}
int main(){
int t,q,l,r,k;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
scanf("%s",s);
get_sa(s,n);
get_height(s,n);
for(int i=n;i>=1;--i) sa[i]=sa[i-1],++sa[i];
for(int i=n;i>=1;--i) Rank[i]=Rank[i-1],++Rank[i];
for(int i=n;i>=1;--i) height[i]=height[i-1];
if(n==1) Rank[1]=1;
// for(int i=1;i<=n;++i) cout<<sa[i]<<" ";
// cout<<endl;
// for(int i=1;i<=n;++i) cout<<Rank[i]<<" ";
// cout<<endl;
// for(int i=1;i<=n;++i) cout<<height[i]<<" ";
// cout<<endl;
tot=0;
build(1,n,1);
root[0]=build(1,n);
for(int i=1;i<=n;++i) root[i]=updata(root[i-1],1,n,sa[i],1);
int ll,rr;
while(q--){
scanf("%d%d%d",&l,&r,&k);
int p=Rank[l];
ll=myfindL(1,n,1,p,r-l+1);
rr=myfindR(1,n,1,p+1,r-l+1);
if(ll==-1) ll=1;
if(rr==-1) rr=n;
else --rr;
//cout<<Rank[l]<<" "<<ll<<" "<<rr<<endl;
if(rr-ll+1<k) printf("-1\n");
else printf("%d\n",myfindk(root[rr],root[ll-1],1,n,k));
}
//for(int i=0;i<=n;++i) Rank[i]=height[i]=sa[i]=0;
}
return 0;
}