传送门
之前用后缀数组+主席树写过:之前的版本
学习了后缀自动机和线段树合并之后再来做一下这道题。
首先对字符串 (s) 建后缀自动机,记录下每个前缀的 endpos 和前缀在 SAM 上所处的位置。
因为 (s) 的子串唯一对应 SAM 上的某一点,这个询问相当于问 parent 树上某一点的子树中第 k 大的 endpos 是多少。
考虑怎么快速确定询问串对应 SAM 上的哪一点,根据 parent 树的性质,(s) 的前缀对应 parent 树的叶节点,因为已知前缀 (s_{1,r}) 所处位置 (x),所以让 (x) 不断向上跳,直到 (len[fa[x]]<r-l+1le len[x]),此时 (x) 就包含了子串 (s_{l,r}),当然这个过程就用树上倍增就可以了。
而询问子树上第 k 大值,可以用线段树合并,也可以用 dfs 序将树拉成区间然后建主席树。
所以后缀自动机就是比后缀数组更灵活,既可以套树算法,也可以拉成区间套区间算法。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
char s[N];
struct SegTrees{
#define mid (l+r>>1)
int sum[N*50],ls[N*50],rs[N*50],tot;
void clear(){
memset(sum,0,sizeof(sum));
memset(ls,0,sizeof(ls));
memset(rs,0,sizeof(rs));
tot=0;
}
void upd(int &id,int l,int r,int pos){
if(!id) id=++tot;
if(l==r) {sum[id]++;return;}
if(pos<=mid) upd(ls[id],l,mid,pos);
else upd(rs[id],mid+1,r,pos);
sum[id]=sum[ls[id]]+sum[rs[id]];
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
int id=++tot;
if(l==r) {sum[id]=sum[x]+sum[y];return id;}
ls[id]=merge(ls[x],ls[y],l,mid);
rs[id]=merge(rs[x],rs[y],mid+1,r);
sum[id]=sum[ls[id]]+sum[rs[id]];
return id;
}
int ask(int id,int l,int r,int k){
if(l==r) return l;
if(k<=sum[ls[id]]) return ask(ls[id],l,mid,k);
else return ask(rs[id],mid+1,r,k-sum[ls[id]]);
}
#undef mid
}trs;
struct SuffixAutoMachine{
int last,tot,len[N*2],fa[N*2],ch[N*2][26],ep[N*2],tpos[N],st[N*2][19];
int head[N*2],to[N*2],nxt[N*2],rt[N*2],total;
void add(int u,int v){to[++total]=v;nxt[total]=head[u];head[u]=total;}
int newnode(int x){fa[++tot]=fa[x];len[tot]=len[x];memcpy(ch[tot],ch[x],sizeof(ch[tot]));return tot;}
void append(int c,int pos){
int p=last,np=last=newnode(0);
len[np]=len[p]+1;ep[np]=pos;tpos[pos]=np;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) {fa[np]=1;return;}
int q=ch[p][c];
if(len[q]==len[p]+1) {fa[np]=q;return;}
int nq=newnode(q);ep[nq]=0;len[nq]=len[p]+1;
fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
void dfs(int u){
if(ep[u]) trs.upd(rt[u],1,n,ep[u]);
for(int i=head[u];i;i=nxt[i]){
dfs(to[i]);
rt[u]=trs.merge(rt[u],rt[to[i]],1,n);
}
}
void init(){
last=tot=total=0;
last=tot=newnode(0);
memset(head,0,sizeof(head));
memset(rt,0,sizeof(rt));
trs.clear();
for(int i=1;i<=n;i++) append(s[i]-'a',i);
for(int i=2;i<=tot;i++) add(fa[i],i),st[i][0]=fa[i];
dfs(1);
for(int i=1;i<19;i++) for(int j=1;j<=tot;j++) st[j][i]=st[st[j][i-1]][i-1];
}
void ask(int l,int r,int k){
int p=tpos[r];
for(int i=18;i>=0;i--) if(len[st[p][i]]>=r-l+1) p=st[p][i];
if(trs.sum[rt[p]]<k) {printf("-1
");return;}
printf("%d
",trs.ask(rt[p],1,n,k)-(r-l));
}
}sam;
void solve(){
scanf("%d%d",&n,&m);
scanf("%s",s+1);
sam.init();
for(int i=1,l,r,k;i<=m;i++){
scanf("%d%d%d",&l,&r,&k);
sam.ask(l,r,k);
}
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}