传送门

另一个传送门

终于把心头大恨切掉了……后缀自动机大法好,从此抛弃后缀数组哈哈……(说的跟你会写后缀数组似的

好像网上的题解大多都是后缀数组?看了看表示理解不能,那我这份后缀自动机的题解就写详细点好了……

题目跟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 字符串的更多相关文章

  1. Bzoj 4556: [Tjoi2016&Heoi2016]字符串

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 177  Solved: 92[Sub ...

  2. Bzoj4556: [Tjoi2016&Heoi2016]字符串 后缀数组

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 169  Solved: 87[Sub ...

  3. 4556: [Tjoi2016&Heoi2016]字符串

    4556: [Tjoi2016&Heoi2016]字符串 链接 分析: 首先可以二分这个长度.此时需要判断是否存在一个以b结尾的前缀,满足与[c,d]的lcp大于等于mid. 如果我们把串翻转 ...

  4. [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MB Description 佳媛姐姐过生日的时候,她的小 ...

  5. 【BZOJ4556】[Tjoi2016&Heoi2016]字符串 后缀数组+二分+主席树+RMQ

    [BZOJ4556][Tjoi2016&Heoi2016]字符串 Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一 ...

  6. [BZOJ4556][TJOI2016&&HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1360  Solved: 545[S ...

  7. [BZOJ4556][Tjoi2016&Heoi2016]字符串 主席树+二分+倍增+后缀自动机

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1215  Solved: 484[S ...

  8. BZOJ4556: [Tjoi2016&Heoi2016]字符串

    Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开 ...

  9. BZOJ4556 [Tjoi2016&Heoi2016]字符串 SA ST表 二分答案 主席树

    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ4556.html 题目传送门 - BZOJ4556 题意 给定一个长度为 $n$ 的字符串 $s$ . ...

  10. 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... ...

随机推荐

  1. 2016级算法第二次上机-E.AlvinZH的儿时梦想——运动员篇

    862-AlvinZH的儿时梦想--运动员篇 思路 难题. 应该想到,不管给出的数据如何,每一个淘汰的人不会对最终答案产生任何影响,所以每次淘汰就把人除掉就可以了,最后剩下的两个人计算它们从开始到相遇 ...

  2. css加载顺序

    最近发现个有意思的事情,印象中的是css中class后面会覆盖前面的, 于是写了代码 div{ width: 100px; height: 100px; } .red{ background-colo ...

  3. python高级(三)—— 字典和集合(泛映射类型)

    本文主要内容 可散列类型 泛映射类型 字典 (1)字典推导式 (2)处理不存在的键 (3)字典的变种 集合 映射的再讨论 python高级——目录 文中代码均放在github上:https://git ...

  4. leetcode 53 最大子序列之和(动态规划)

    思路:nums为给定的数组,动态规划: 设 一维数组:dp[i] 表示 以第i个元素为结尾的一段最大子序和. 1)若dp[i-1]小于0,则dp[i]加上前面的任意长度的序列和都会小于nums[i], ...

  5. BZOJ - 3263 三维偏序

    题意:定义元素为有序组(a,b,c),若存在x组(a_i,b_i,c_i)分别小于等于(a,b,c),则该元素的等级为x,求[0,n-1]等级的个数 cdq分治练手题,对a简单排序并去重,对b进行分治 ...

  6. dedicated bearer

    Session Initiation Protocol (SIP) Basic Call Flow ExamplesRFC3665 intuitive,   done. PDF] TS 123 401 ...

  7. [转] Centos 系统swap虚拟内存添加与删除配置

    [From]https://blog.csdn.net/lengyue1084/article/details/51405640 [From]https://yuukis.cn/24/ SWAP是Li ...

  8. hive DML

    1.load files into tables 把文件中的数据加载到表中(表必须先建好) 语法是: load data [local] inpath 'filepath' [overwrite] i ...

  9. atoi和stoi

    vs环境下:stoi函数默认要求输入的参数字符串是符合int范围的[-2147483648, 2147483647],否则会runtime error.atoi函数则不做范围检查,若超过int范围,则 ...

  10. ubuntu 16.04 屏幕截图

    shift+prtsc   快捷键截取指定区域的图片 类似qq截图