题意:
给定长度为n的串,q次询问。
每次询问给出L,R,k,
问子串[L,R]在串中第k次出现的起点,如果没有第k次则输出-1
数据范围:n,q<=1e5
解法:
先跑后缀数组
子串[L,R]是suf[L]的前缀,排名为rk[L]
在height[]上建立st表,
在height[]上二分出height[rk[L]]左右两边,包含子串[L,R]的sa[]的范围[st,ed]
接下来就是求sa[st,ed]中的第k大了,用可持久化线段树搞
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
struct SA{
static const int N=1e5+5;
char s[N];
int sa[N],rk[N],oldrk[N<<1],id[N],px[N],cnt[N];
int ht[N];
int n;//字符串长度
int m=300;//初始字符集大小
void init(){//init
memset(cnt,0,sizeof cnt);
m=300;
}
bool cmp(int x,int y,int w){
return oldrk[x]==oldrk[y]&&oldrk[x+w]==oldrk[y+w];
}
void getSA(){
int i,p,w;
for(i=1;i<=n;i++)cnt[rk[i]=s[i]]++;
for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
//
for(w=1;w<n;w<<=1,m=p){
for(p=0,i=n;i>n-w;i--)id[++p]=i;
for(i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
memset(cnt,0,sizeof cnt);
for(i=1;i<=n;i++)cnt[px[i]=rk[id[i]]]++;
for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--)sa[cnt[px[i]]--]=id[i];
memcpy(oldrk,rk,sizeof rk);
for(p=0,i=1;i<=n;i++){
rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
}
}
}
void getHT(){
for(int i=1,k=0;i<=n;i++){
if(k)k--;
while(s[i+k]==s[sa[rk[i]-1]+k])k++;
ht[rk[i]]=k;
}
}
//
static const int maxd=(int)(log(N)/log(2))+1;
int mi[N][maxd+5];
void getRMQ(){//在ht[]上建立st表
for(int i=1;i<=n;i++){
mi[i][0]=ht[i];
}
for(int j=1;j<=maxd;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
}
}
}
int askmi(int l,int r){
int k=log2(r-l+1);
return min(mi[l][k],mi[r-(1<<k)+1][k]);
}
//
}sa;
int lc[maxm*40],rc[maxm*40],cnt[maxm*40];
int rt[maxm],tot;
void init(){
tot=0;
rt[0]=0;
lc[0]=rc[0]=0;
cnt[0]=0;
}
void update(int x,int l,int r,int last,int &k){
k=++tot;
lc[k]=lc[last];
rc[k]=rc[last];
cnt[k]=cnt[last];
cnt[k]++;
if(l==r)return ;
int mid=(l+r)/2;
if(x<=mid)update(x,l,mid,lc[last],lc[k]);
else update(x,mid+1,r,rc[last],rc[k]);
}
int ask(int k,int l,int r,int L,int R){
if(l==r)return l;
int mid=(l+r)/2;
int lcc=cnt[lc[R]]-cnt[lc[L]];
if(lcc>=k){
return ask(k,l,mid,lc[L],lc[R]);
}else{
k-=lcc;
return ask(k,mid+1,r,rc[L],rc[R]);
}
}
signed main(){
int T;cin>>T;
while(T--){
//init
init();
sa.init();
char *s=sa.s;
int &n=sa.n;
int *ss=sa.sa;
int *rk=sa.rk;
//input
int q;
scanf("%d%d",&n,&q);
scanf("%s",s+1);
//SA
sa.getSA();
sa.getHT();
sa.getRMQ();
//在sa[]上建可持久化线段树
for(int i=1;i<=n;i++){
update(ss[i],1,n,rt[i-1],rt[i]);
}
//
while(q--){
int ql,qr,k;scanf("%d%d%d",&ql,&qr,&k);
int len=qr-ql+1;
int L=rk[ql],R=rk[ql];
int l=1,r=rk[ql]-1;
while(l<=r){
int mid=(l+r)/2;
if(sa.askmi(mid+1,rk[ql])>=len){//lcp是[mid+1,rk[ql]]之间height的min
L=mid,r=mid-1;
}else{
l=mid+1;
}
}
l=rk[ql]+1,r=n;
while(l<=r){
int mid=(l+r)/2;
if(sa.askmi(rk[ql]+1,mid)>=len){
R=mid,l=mid+1;
}else{
r=mid-1;
}
}
if(R-L+1<k){
puts("-1");continue;
}
int ans=ask(k,1,n,rt[L-1],rt[R]);
printf("%d\n",ans);
}
}
return 0;
}