Tjoi2016&Heoi2016 字符串
终于把心头大恨切掉了……后缀自动机大法好,从此抛弃后缀数组哈哈……(说的跟你会写后缀数组似的
好像网上的题解大多都是后缀数组?看了看表示理解不能,那我这份后缀自动机的题解就写详细点好了……
题目跟LCP有关,不难想到后缀树,对反串建后缀自动机之后得到的parent树就是原串的后缀树,之后的操作就在parent树上乱搞就行了。
询问都是询问s[a..b]中的所有子串和s[c..d]的LCP长度的最大值,显然s[a..b]的子串可以直接改成s[a..b]的后缀,那么就有
$ans=\min\{\max_{a\le i\le b}\{\min\{LCP(i,c),b-i+1\}\},d-c+1\}$
记黑点为每个前缀对应的节点,如果没有b-i+1的限制的话,问题就变成了每次询问c与所有编号位于[a,b]的黑点的所有LCA中深度最大的那一个的深度,显然是可以直接上主席树+倍增的,单次询问$O(log^2n)$(@树白黑)。
现在有了b-i+1的限制,可以二分答案,设当前答案为M,任务就变成了判定答案能否$\ge M$。显然只有$b-i+1\ge M$的i合法(即可以使答案$\ge M$),移项得$i\le b-M+1$,再加上$a\le i\le b$的限制即可得出合法的i的范围,再用倍增找到最浅的深度$\ge M$的点(因为这个点要作为深度最小的LCA,或者是这个LCA的祖先),询问一下这个点的子树中是否存在一个编号在合法范围内的黑点即可(因为这个点一定是c的祖先,因此只要子树中有黑点就说明深度最小的LCA不会比它浅),有则说明答案$\ge M$,否则说明答案<M,调整下一次二分即可。
询问子树中是否有黑点可以用主席树,那么每次判定的复杂度就是倍增$O(logn)$+主席树$O(logn)$=$O(logn)$,加上二分答案后单次询问$O(log^2n)$,还是在线算法(虽然大多都是在线不过听说有写离线的……?)
代码里的二分可以保证$M\ge 1$,因此没有判$i\le b$的限制。
/**************************************************************
Problem: 4556
User: hzoier
Language: C++
Result: Accepted
Time:12996 ms
Memory:120180 kb
****************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=;
void expand(int);
void dfs(int);
void build(int,int,int&,int);
void query(int,int,int,int);
vector<int>G[maxn];
int SAM_root,last,SAM_cnt=,val[maxn]={},par[maxn]={},go[maxn][]={{}};
int sm[maxn<<],lc[maxn<<],rc[maxn<<]={},cnt=,root[maxn]={};
int f[maxn][]={{}},dfn[maxn],finish[maxn],tim=;
char S[maxn];
int n,m,iter[maxn],k=,a,b,c,d=,x,r,s,t,tmp;
int main(){
SAM_root=last=++SAM_cnt;
scanf("%d%d%s",&n,&m,S+);
for(int i=n;i;i--){
expand(S[i]-'a');
iter[i]=last;
}
for(int i=;i<=SAM_cnt;i++)G[par[i]].push_back(i);
dfs(SAM_root);
for(int i=;i<=n;i++){
x=dfn[iter[i]];
build(,tim,root[i],root[i-]);
}
for(int j=;j<=k;j++)for(int i=;i<=tim;i++)f[i][j]=f[f[i][j-]][j-];
while(m--){
scanf("%d%d%d%d",&a,&b,&c,&d);
int L=,R=b-a+;
while(L<=R){
int M=(L+R)>>;
x=iter[c];
tmp=;
if(val[x]>=M){
for(int i=k;i>=;i--)if(val[f[x][i]]>=M)x=f[x][i];
s=dfn[x];
t=finish[x];
if(a<=b-M+)query(,tim,root[b-M+],root[a-]);
}
if(tmp)L=M+;
else R=M-;
}
printf("%d\n",min(R,d-c+));
}
return ;
}
void expand(int c){
int p=last,np=++SAM_cnt;
val[np]=val[p]+;
while(p&&!go[p][c]){
go[p][c]=np;
p=par[p];
}
if(!p)par[np]=SAM_root;
else{
int q=go[p][c];
if(val[q]==val[p]+)par[np]=q;
else{
int nq=++SAM_cnt;
val[nq]=val[p]+;
memcpy(go[nq],go[q],sizeof(go[q]));
par[nq]=par[q];
par[np]=par[q]=nq;
while(p&&go[p][c]==q){
go[p][c]=nq;
p=par[p];
}
}
}
last=np;
}
void dfs(int x){
dfn[x]=++tim;
d++;
while((<<k)<d)k++;
for(int i=;i<(int)G[x].size();i++){
f[G[x][i]][]=x;
dfs(G[x][i]);
}
finish[x]=tim;
d--;
}
void build(int l,int r,int &rt,int pr){
sm[rt=++cnt]=sm[pr]+;
if(l==r)return;
lc[rt]=lc[pr];
rc[rt]=rc[pr];
int mid=(l+r)>>;
if(x<=mid)build(l,mid,lc[rt],lc[pr]);
else build(mid+,r,rc[rt],rc[pr]);
}
void query(int l,int r,int rt,int pr){
if(!rt&&!pr)return;
if(s<=l&&t>=r){
tmp+=sm[rt]-sm[pr];
return;
}
int mid=(l+r)>>;
if(s<=mid)query(l,mid,lc[rt],lc[pr]);
if(t>mid)query(mid+,r,rc[rt],rc[pr]);
}
一个细节:
一开始觉得二分答案可以直接换成一边倍增上跳一边判定当前点是否可行,后来发现这样是错的,因为答案不一定是c的某个祖先的深度(比如有个点深度是2,父亲的深度是0,可是答案是1)……当然判定当前点是否可行的时候再二分一下也可以,不过这样好像会多一个log……
话说很久之前就想写这题了,然后题意各种弄不清+网上的后缀数组题解各种看不懂=无限期跳票,今天心血来潮读了一遍题才弄清题意,然后找了几份后缀数组的题解还是没怎么看懂……无奈自己脑补了一发后缀自动机的做法,然而为啥跑得这么慢……明明是同样的做法,我比ad学长慢了整整2s,比后缀数组众更是慢到不知哪里去了……
Tjoi2016&Heoi2016 字符串的更多相关文章
- Bzoj 4556: [Tjoi2016&Heoi2016]字符串
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 177 Solved: 92[Sub ...
- Bzoj4556: [Tjoi2016&Heoi2016]字符串 后缀数组
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 169 Solved: 87[Sub ...
- 4556: [Tjoi2016&Heoi2016]字符串
4556: [Tjoi2016&Heoi2016]字符串 链接 分析: 首先可以二分这个长度.此时需要判断是否存在一个以b结尾的前缀,满足与[c,d]的lcp大于等于mid. 如果我们把串翻转 ...
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MB Description 佳媛姐姐过生日的时候,她的小 ...
- 【BZOJ4556】[Tjoi2016&Heoi2016]字符串 后缀数组+二分+主席树+RMQ
[BZOJ4556][Tjoi2016&Heoi2016]字符串 Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一 ...
- [BZOJ4556][TJOI2016&&HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1360 Solved: 545[S ...
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 主席树+二分+倍增+后缀自动机
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1215 Solved: 484[S ...
- BZOJ4556: [Tjoi2016&Heoi2016]字符串
Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开 ...
- BZOJ4556 [Tjoi2016&Heoi2016]字符串 SA ST表 二分答案 主席树
原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ4556.html 题目传送门 - BZOJ4556 题意 给定一个长度为 $n$ 的字符串 $s$ . ...
- 2019.02.27 bzoj4556: [Tjoi2016&Heoi2016]字符串(二分答案+sam+线段树合并)
传送门 题意:给一个字符串SSS. 有mmm次询问,每次给四个参数a,b,c,da,b,c,da,b,c,d,问s[a...b]s[a...b]s[a...b]的所有子串和s[x...y]s[x... ...
随机推荐
- 【数据结构】10分钟教你用栈求解迷宫老鼠问题超详细教程附C++源代码
问题描述 给定一张迷宫地图和一个迷宫入口,然后进入迷宫探索找到一个出口.如下图所示: 该图是一个矩形区域,有一个入口和出口.迷宫内部包含不能穿越的墙壁或者障碍物.这些障碍物沿着行和列放置,与迷宫的边界 ...
- git 克隆 提交本地修改到远程方法
最近一个项目,提交总报错 按照下面的流程就ok了 $ git clone $ git init $ cd shop $ git branch -al //查看所有分支 $ git pull origi ...
- 项目Alpha冲刺 6
作业描述 课程: 软件工程1916|W(福州大学) 作业要求: 项目Alpha冲刺(团队) 团队名称: 火鸡堂 作业目标: 介绍第6天冲刺的项目进展.问题困难和心得体会 1.团队信息 队名:火鸡堂 队 ...
- win10安装ubuntu子系统配置
# 更改apt源 cat > /etc/apt/sources.list <<'END' #阿里云 deb http://mirrors.aliyun.com/ubuntu/ tru ...
- C++ 流控制函数setw()、setfill()、setbase()、setprecision()的使用
头文件: #include <iostream> #include <iomanip> 功能: std::setw :需要填充多少个字符,默认填充的字符为' '空格 std:: ...
- activeMQ入门+spring boot整合activeMQ
最近想要学习MOM(消息中间件:Message Oriented Middleware),就从比较基础的activeMQ学起,rabbitMQ.zeroMQ.rocketMQ.Kafka等后续再去学习 ...
- CSAPP阅读笔记-汇编语言初探(控制类指令)-来自第三章3.6的笔记-P135-P163
1.正溢出与负溢出: 首先,一个正数与一个负数相加,不可能溢出,因为结果的绝对值一定小于两个加数的绝对值,既然两个加数能合理表示出来,结果一定也能合理表示出来. 其次,正溢出是由于两个很大的正数相加, ...
- Fiddler使用一(Fiddler简介)
参考文章:http://blog.csdn.net/ohmygirl/article/details/17846199 1.为什么是Fiddler? 抓包工具有很多,小到最常用的web调试工具fire ...
- sencha touch overlay 里使用 list
1 sencha touch 中 list 如果不设置一个固定高度或 flex : 1, list 的内容就不会显示出来. 主要是因为 list 是可滚动的,你不设置高度 ,list 的高度默认就是 ...
- Microsoft Azure 配置负载均衡
负载均衡大家都知道是什么了吧,就不用介绍了.如何在 microsoft Azure 上配置了?创建两台虚拟机,并加入如云服务.然后在远处登录上去,有时你想在两台虚拟机上通信,怎么办,比如mymv2 p ...