Hdu 6704 K-th occurrence【SAM+线段树合并+树上倍增】
时间:2020-04-11
本文章向大家介绍Hdu 6704 K-th occurrence【SAM+线段树合并+树上倍增】,主要包括Hdu 6704 K-th occurrence【SAM+线段树合并+树上倍增】使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
传送门
之前用后缀数组+主席树写过:之前的版本
学习了后缀自动机和线段树合并之后再来做一下这道题。
首先对字符串 \(s\) 建后缀自动机,记录下每个前缀的 endpos 和前缀在 SAM 上所处的位置。
因为 \(s\) 的子串唯一对应 SAM 上的某一点,这个询问相当于问 parent 树上某一点的子树中第 k 大的 endpos 是多少。
考虑怎么快速确定询问串对应 SAM 上的哪一点,根据 parent 树的性质,\(s\) 的前缀对应 parent 树的叶节点,因为已知前缀 \(s_{1,r}\) 所处位置 \(x\),所以让 \(x\) 不断向上跳,直到 \(len[fa[x]]<r-l+1\le 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\n");return;}
printf("%d\n",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;
}
原文地址:https://www.cnblogs.com/BakaCirno/p/12679997.html
- JMeter函数和变量11
- JMeter监听器10
- JMeter处理器09
- 保存带有emoji的文本报错解决方案
- Python:set集合、深浅拷贝与函数
- Python:numpy总结(4)
- Python: numpy总结(2)
- class 类—老司机的必修课 | 统计师的Python日记 第11课
- Python:matplotlib
- Python: matplotlib安装
- Java后端实现图片压缩技术(赞赏功能已开通,欢迎测试,噗~!)
- 我是如何得知10W+的访问量多来自工作日的 | 塔秘
- 使用百度UMeditor富文本编辑器,修改自定义图片上传,修改源码
- 【技术专栏】OpenVirteX体系结构之组件(一)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法