题意:给你一个长度为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. StringRedisTemplate与RedisTemplate区别

    StringRedisTemplate与RedisTemplate两者的关系是StringRedisTemplate继承RedisTemplate. 两者的数据是不共通的:也就是说StringRedi ...

  2. HCL试验9

    PC1配置: ip:192.168.1.1 掩码:255.255.255.0 网关:192.168.1.254 上路由器配置: sys int gi0/0 ip add 192.168.100.10 ...

  3. bean初始化和销毁的几种方式

    Bean生命周期 Bean创建 -->初始化 -->销毁 1.自定义Bean初始化 和销毁的方法 init-method和destroy-method 创建Bike类 public cla ...

  4. python基础--合并两个列表

    a = [1,2,3] b = [4,5,6] # 将两个列表合并 # append方法是不行的 # a.append(b) # print(a) # [1, 2, 3, [4, 5, 6]] # 我 ...

  5. base64图片数据类型转numpy的ndarray矩阵类型数据

    1.两种方法如下链接 https://www.cnblogs.com/mtcnn/p/9411683.html 2.第一种方法: # coding: utf-8 # python base64 编解码 ...

  6. PTA(Basic Level)1058.A+B in Hogwarts

    If you are a fan of Harry Potter, you would know the world of magic has its own currency system -- a ...

  7. [转帖]CENTOS6.6下mysql5.7.11带boost和不带boost的源码安装

    CENTOS6.6下mysql5.7.11带boost和不带boost的源码安装 本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuir ...

  8. linux 三剑客之awk总结

    AWK 1.begin end使用 cat /tmp/passwd |awk -F ':' 'BEGIN {print "hello"} {print $1"\t&quo ...

  9. Dango之模版系统

    1.模板渲染 可以传列表,字典,对象等 {{ 变量 }} {% 逻辑 %} -- 标签 urls.py path('login/', views.login), views.py def login( ...

  10. 基于apache-commons-email1.4 邮件发送

    MailUtil.java package com.lucky.base.common.util; import com.zuche.framework.utils.PropertiesReader; ...