题意:给你一个长度为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 ;
}

HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)的更多相关文章

  1. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  2. [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树

    二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...

  3. hdu 5029 Relief grain(树链剖分+线段树)

    题目链接:hdu 5029 Relief grain 题目大意:给定一棵树,然后每次操作在uv路径上为每一个节点加入一个数w,最后输出每一个节点个数最多的那个数. 解题思路:由于是在树的路径上做操作, ...

  4. hdu 4117 GRE Words (ac自动机 线段树 dp)

    参考:http://blog.csdn.net/no__stop/article/details/12287843 此题利用了ac自动机fail树的性质,fail指针建立为树,表示父节点是孩子节点的后 ...

  5. Aragorn's Story 树链剖分+线段树 && 树链剖分+树状数组

    Aragorn's Story 来源:http://www.fjutacm.com/Problem.jsp?pid=2710来源:http://acm.hdu.edu.cn/showproblem.p ...

  6. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  7. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  8. 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)

    题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...

  9. HDU 1611 敌兵布阵 / HRBUST 1794 敌兵布阵(线段树)

    HDU 1611 敌兵布阵 / HRBUST 1794 敌兵布阵(线段树) Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A ...

随机推荐

  1. Beego开启热升级

    1.打开配置 beego.BConfig.Listen.Graceful = true 2.写入pid 程序入口main()函数里写入pid func writePid() { fileName := ...

  2. Java并发编程之程序运行堆栈分析

    Java程序运行的堆栈分析 1.JVM运行时数据区 JVM通过加载class文件的数据来执行程序.JVM在运行时会划分不同的区域以存放数据.如下图所示: 线程共享部分:所有线程都能访问这块内存的数据, ...

  3. axios在Vue中的简单应用(一)

    1.安装axios: npm install --save axios vue-axios 2.安装qs: qs.stringify(data)可以解决data数据格式问题 npm install - ...

  4. 串的应用与kmp算法讲解--学习笔记

    串的应用与kmp算法讲解 1. 写作目的 平时学习总结的学习笔记,方便自己理解加深印象.同时希望可以帮到正在学习这方面知识的同学,可以相互学习.新手上路请多关照,如果问题还请不吝赐教. 2. 串的逻辑 ...

  5. input.validity

    HTML5为表单提供了自带的输入规则提示 但是实际开发中,我们往往需要自定义提示消息和规则,例如限定了最大值但不要求超出时提示错误信息,这时便用到了H5提供的表单新的JS属性--validity,它是 ...

  6. 渗透测试 - KALI Linux 学习 - kali linux如何启动METASPLOIT服务

    kali 2.0 已经没有metasploit 这个服务了,所以service metasploit start 的方式不起作用. 在kali 2.0中启动带数据库支持的MSF方式如下: #1  首先 ...

  7. Star all over again.

    0x00前言 经过了一上午的折腾之后,博客的界面勉强可观,今天下午将之前的所有博客全部删除,重新开始写属于自己的博客,而不是只把它当作一个收藏夹,转载其他人的文章. 0x01近来感想 有感而发,随便写 ...

  8. 【转载】深度学习中softmax交叉熵损失函数的理解

    深度学习中softmax交叉熵损失函数的理解 2018-08-11 23:49:43 lilong117194 阅读数 5198更多 分类专栏: Deep learning   版权声明:本文为博主原 ...

  9. LINQ查询表达式详解(2)——查询表达式的转换

    简介 C#在执行LINQ查询表达式的时候,并不会指定其执行语义,而是将查询表达式转换为遵循查询表达式模式的方法的调用.具体而言,查询表达式将转换为以下名称的调用:Where.Select.Select ...

  10. Laravel策略(Policy)示例

    场景:当前用户创建的订单,只能当前用户自己看,可以通过授权策略类(Policy)来实现 1.php artisan make:policy OrderPolicy 成功后,默认只有一个构造方法.因为涉 ...