题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点。

解法一:

求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+,mod=;
char buf[N];
int s[N],sa[N],buf1[N],buf2[N],c[N],n,rnk[N],ht[N],ST[N][],Log[N],m;
void Sort(int* x,int* y,int m) {
for(int i=; i<m; ++i)c[i]=;
for(int i=; i<n; ++i)++c[x[i]];
for(int i=; i<m; ++i)c[i]+=c[i-];
for(int i=n-; i>=; --i)sa[--c[x[y[i]]]]=y[i];
}
void da(int* s,int n,int m=) {
int *x=buf1,*y=buf2;
x[n]=y[n]=-;
for(int i=; i<n; ++i)x[i]=s[i],y[i]=i;
Sort(x,y,m);
for(int k=; k<n; k<<=) {
int p=;
for(int i=n-k; i<n; ++i)y[p++]=i;
for(int i=; i<n; ++i)if(sa[i]>=k)y[p++]=sa[i]-k;
Sort(x,y,m),p=,y[sa[]]=;
for(int i=; i<n; ++i)y[sa[i]]=x[sa[i-]]==x[sa[i]]&&x[sa[i-]+k]==x[sa[i]+k]?p-:p++;
if(p==n)break;
swap(x,y),m=p;
}
}
void getht() {
for(int i=; i<n; ++i)rnk[sa[i]]=i;
ht[]=;
for(int i=,k=; i<n; ++i) {
if(k)--k;
if(!rnk[i])continue;
for(; s[i+k]==s[sa[rnk[i]-]+k]; ++k);
ht[rnk[i]]=k;
}
}
void initST() {
for(int i=; i<n; ++i)ST[i][]=ht[i];
for(int j=; (<<j)<=n; ++j)
for(int i=; i+(<<j)-<n; ++i)
ST[i][j]=min(ST[i][j-],ST[i+(<<(j-))][j-]);
}
int lcp(int l,int r) {
if(l==r)return n-sa[l];
if(l>r)swap(l,r);
l++;
int k=Log[r-l+];
return min(ST[l][k],ST[r-(<<k)+][k]);
}
int rt[N],ls[N*],rs[N*],sum[N*],tot;
#define mid ((l+r)>>1)
int cpy(int v) {int u=++tot; ls[u]=ls[v],rs[u]=rs[v],sum[u]=sum[v]; return u;}
void upd(int& u,int v,int p,int l=,int r=n-) {
u=cpy(v);
sum[u]++;
if(l==r)return;
p<=mid?upd(ls[u],ls[v],p,l,mid):upd(rs[u],rs[v],p,mid+,r);
}
int qry(int u,int v,int k,int l=,int r=n-) {
if(l==r)return l;
int cnt=sum[ls[u]]-sum[ls[v]];
return k<=cnt?qry(ls[u],ls[v],k,l,mid):qry(rs[u],rs[v],k-cnt,mid+,r);
}
void build() {
da(s,n),getht(),initST();
tot=;
for(int i=; i<=n; ++i)upd(rt[i],rt[i-],sa[i-]);
}
int main() {
Log[]=-;
for(int i=; i<N; ++i)Log[i]=Log[i>>]+;
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d%s",&n,&m,buf);
for(int i=; i<n; ++i)s[i]=buf[i];
s[n]=;
build();
while(m--) {
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
l--,r--;
int L,R,M=rnk[l],len=r-l+;
L=R=M;
for(int i=Log[n]; i>=; --i)if(L-(<<i)>=&&lcp(M,L-(<<i))>=len)L-=(<<i);
for(int i=Log[n]; i>=; --i)if(R+(<<i)<n&&lcp(M,R+(<<i))>=len)R+=(<<i);
printf("%d\n",k<=sum[rt[R+]]-sum[rt[L]]?qry(rt[R+],rt[L],k)+:-);
}
}
return ;
}

解法二:

建立后缀自动机,对后缀树(fail树)作线段树合并可得到每个结点包含的全部right值。对每个询问倍增找到待查询子串所对应的结点,然后线段树上查询第k大即可。

可持久化合并可以实现在线查询。

fail树上dfs序建可持久化线段树貌似也可以(这句话怎么这么耳熟?)

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+,M=;
char s[N];
int n,m,fa[N],go[N][M],mxl[N],last,tot,samrt[N],c[N],ss[N],Fa[N][];
int rt[N],ls[N*],rs[N*],sum[N*],tot2;
#define mid ((l+r)>>1)
int cpy(int v) {int u=++tot2; ls[u]=ls[v],rs[u]=rs[v],sum[u]=sum[v]; return u;}
void upd(int& u,int v,int p,int l=,int r=n) {
u=cpy(v),sum[u]++;
if(l==r)return;
p<=mid?upd(ls[u],ls[v],p,l,mid):upd(rs[u],rs[v],p,mid+,r);
}
void mg(int& w,int u,int v) {
if(!u||!v) {w=u|v; return;}
w=cpy(u),sum[w]+=sum[v];
mg(ls[w],ls[u],ls[v]),mg(rs[w],rs[u],rs[v]);
}
int qry(int u,int k,int l=,int r=n) {
if(l==r)return l;
return k<=sum[ls[u]]?qry(ls[u],k,l,mid):qry(rs[u],k-sum[ls[u]],mid+,r);
}
int newnode(int l) {int u=++tot; rt[u]=,mxl[u]=l,memset(go[u],,sizeof go[u]); return u;}
void add(int ch,int r) {
int p=last,np=last=newnode(mxl[p]+);
samrt[r]=np;
upd(rt[np],rt[np],r,);
for(; p&&!go[p][ch]; p=fa[p])go[p][ch]=np;
if(!p)fa[np]=;
else {
int q=go[p][ch];
if(mxl[q]==mxl[p]+)fa[np]=q;
else {
int nq=newnode(mxl[p]+);
memcpy(go[nq],go[q],sizeof go[q]);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(; p&&go[p][ch]==q; p=fa[p])go[p][ch]=nq;
}
}
}
void build() {
tot=tot2=,last=newnode();
for(int i=; i<n; ++i)add(s[i]-'a',i+);
for(int i=; i<=tot; ++i)c[i]=;
for(int i=; i<=tot; ++i)++c[mxl[i]];
for(int i=; i<=tot; ++i)c[i]+=c[i-];
for(int i=; i<=tot; ++i)ss[--c[mxl[i]]]=i;
for(int i=tot-; i>; --i)mg(rt[fa[ss[i]]],rt[fa[ss[i]]],rt[ss[i]]);
for(int i=; i<=tot; ++i)Fa[i][]=fa[i];
for(int k=; k<; ++k)for(int i=; i<=tot; ++i)Fa[i][k]=Fa[Fa[i][k-]][k-];
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d%s",&n,&m,s);
build();
while(m--) {
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
int u=samrt[r],len=r-l+;
if(mxl[fa[u]]+>len) {
for(int k=; k>=; --k)if(mxl[fa[Fa[u][k]]]+>len)u=Fa[u][k];
u=fa[u];
}
printf("%d\n",k<=sum[rt[u]]?qry(rt[u],k)-len+:-);
}
}
return ;
}

最新文章

  1. iOS 解决图片上传到服务器旋转90度的问题(图片倒置)
  2. 网页制作过程中div定位的三个问题
  3. Reading With Purpose: A grand experiment
  4. android中include 的使用讲解
  5. 导航栏全透明效果, 只保留左右两个按钮, 如何实现?以及关于NavigationController的小问题
  6. jquery ajax POST 例子详解
  7. vs2010自带的报表
  8. python学习笔记--Django入门二 Django 的模板系统
  9. [spring-framework]Spring定时器的配置和使用
  10. Php 常用类
  11. unity碰撞组件、刚体组件
  12. Android异步任务
  13. windows2008 配置安装FTP服务器
  14. 解决虚拟机vmware安装64位系统“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”的问题
  15. MUI框架开发HTML5手机APP(一)--搭建第一个手机APP
  16. bzoj 4819: [Sdoi2017]新生舞会
  17. 关于 web 页面 占满全屏
  18. 无用之flask学习
  19. BZOJ1712 : [Usaco2007 China]Summing Sums 加密
  20. Maven 搭建 SSM框架——Spring+SpringMVC+Mybatis的搭建教程

热门文章

  1. SSO单点登录统一身份认证系统
  2. JavaScript基础入门09
  3. Java并发编程之程序运行堆栈分析
  4. centos 6.5安装erlang和RabbitMQ
  5. LINQ查询表达式详解(2)——查询表达式的转换
  6. SolidWorks学习笔记7 镜像,阵列
  7. splite与join
  8. 搞懂MySQL GTID原理
  9. Python之模块IO
  10. vs nuget找不到包