Description

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了

一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE

O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公

共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

Input

输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来

m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,

字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n

Output

对于每一次询问,输出答案。

Sample Input

5 5

aaaaa

1 1 1 5

1 5 1 1

2 3 2 3

2 4 2 3

2 3 2 4

Sample Output

1

1

2

2

2


首先感觉不好处理前缀,就可以把串反转一下变成查询后缀

然后这样怎么办吗?

考虑二分最长公共后缀的长度,然后我们就只需要判断在区间\([a,b]\)中有没有这样一个子串

那么我们就可以找到这个子串所在的节点,判断这个节点的right集合中有没有位置在\([a+长度-1,b]\)之间的

这样就可以了

但是我们要怎么找到子串所在的节点呢?首先这个节点一定在parent树上是d对应节点的父亲,所以就可以考虑倍增

然后就可以\(O(logn)\)得出子串对应的节点

那么怎么判断right集合中是否存在对应的位置呢?

考虑用线段树合并就可以了

然后总复杂度\(O(nlog^2n)\)

#include<bits/stdc++.h>
using namespace std;
#define IL inline
#define fu(a,b,c) for(int a=b;a<=c;++a)
#define fd(a,b,c) for(int a=b;a>=c;--a)
const int CHARSET_SIZE=26;
const int N=2e5+10;
const int M=4e6+10;
IL int read(){
int ans=0,w=1;char c=getchar();
while(!isdigit(c)&&c!='-')c=getchar();
if(c=='-')w=-1,c=getchar();
while(isdigit(c))ans=(ans<<1)+(ans<<3)+c-'0',c=getchar();
return ans*w;
}
int n,m;char s[N];
//Segment_Tree
int tot=0,rt[M],ld[M],rd[M];
void insert(int &t,int l,int r,int pos){
t=++tot;
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)insert(ld[t],l,mid,pos);
else insert(rd[t],mid+1,r,pos);
}
int merge(int x,int y){
if(!x||!y)return x|y;
int z=++tot;
ld[z]=merge(ld[x],ld[y]);
rd[z]=merge(rd[x],rd[y]);
return z;
}
bool query(int t,int l,int r,int L,int R){
if(!t)return 0;
if(L<=l&&r<=R)return 1;
int mid=(l+r)>>1;
if(R<=mid)return query(ld[t],l,mid,L,R);
if(L>mid)return query(rd[t],mid+1,r,L,R);
return query(ld[t],l,mid,L,mid)||query(rd[t],mid+1,r,mid+1,R);
}
//Suffix_Automaton
struct Node{
int ch[CHARSET_SIZE],prt;
int maxl,right;
Node(int maxl=0,int right=0):ch(),prt(0),maxl(maxl),right(right){}
}t[N];
int root,last,cur;
int topo[N],buc[N],pos[N];
int newnode(int maxl=0,int right=0){t[++cur]=Node(maxl,right);return cur;}
void init(){cur=0;root=last=newnode();}
void extend(int c,int id){
int u=newnode(t[last].maxl+1,1),v=last;
insert(rt[u],1,n,id);pos[id]=u;
for(;v&&!t[v].ch[c];v=t[v].prt)t[v].ch[c]=u;
if(!v){t[u].prt=root;}
else if(t[v].maxl+1==t[t[v].ch[c]].maxl){
t[u].prt=t[v].ch[c];
}else{
int n=newnode(t[v].maxl+1,0),o=t[v].ch[c];
memcpy(t[n].ch,t[o].ch,sizeof(t[o].ch));
t[n].prt=t[o].prt;
t[o].prt=t[u].prt=n;
for(;v&&t[v].ch[c]==o;v=t[v].prt)t[v].ch[c]=n;
}
last=u;
}
void toposort(){
int maxv=0;
fu(i,1,cur){
buc[t[i].maxl]++;
maxv=max(maxv,t[i].maxl);
}
fu(i,1,maxv)buc[i]+=buc[i-1];
fu(i,1,cur)topo[buc[t[i].maxl]--]=i;
fu(i,1,maxv)buc[i]=0;
}
//ST table
int F[N][20];
void get_st_table(){
fu(i,1,cur)F[i][0]=t[i].prt;
fu(j,1,18)
fu(i,1,cur)
F[i][j]=F[F[i][j-1]][j-1];
}
//Main_Solve
void solve(){
fd(i,cur,1){
int u=topo[i],f=F[u][0];
rt[f]=merge(rt[f],rt[u]);
}
}
int main(){
n=read();m=read();
scanf("%s",s+1);
reverse(s+1,s+n+1);
init();
fu(i,1,n)extend(s[i]-'a',i);
toposort();
get_st_table();
solve();
while(m--){
int a=n-read()+1,b=n-read()+1,c=n-read()+1,d=n-read()+1;
swap(a,b);swap(c,d);
int l=1,r=min(b-a+1,d-c+1),ans=0;
while(l<=r){
int mid=(l+r)>>1,p=pos[d];
fd(i,18,0)if(t[F[p][i]].maxl>=mid)p=F[p][i];
if(query(rt[p],1,n,a+mid-1,b))ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}

BZOJ4556 Tjoi2016&Heoi2016 字符串【后缀自动机+倍增+线段树合并】的更多相关文章

  1. [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)

    后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...

  2. 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并

    [CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...

  3. bzoj4556: [Tjoi2016&Heoi2016]字符串 (后缀数组加主席树)

    题目是给出一个字符串,每次询问一个区间[a,b]中所有的子串和另一个区间[c,d]的lcp最大值,首先求出后缀数组,对于lcp的最大值肯定是rank[c]的前驱和后继,但是对于这个题会出现问题,就是题 ...

  4. 【BZOJ3413】匹配(后缀自动机,线段树合并)

    [BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...

  5. 【CF666E】Forensic Examination(后缀自动机,线段树合并)

    [CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ...

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

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

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

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

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

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

  9. CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增

    题意: 给定一个串 $S$ 和若干个串 $T_{i}$每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号. 数据范围: 需要离线 题解:首先,很常 ...

随机推荐

  1. HDU 5763 Another Meaning(DP+KMP)

    http://acm.hdu.edu.cn/showproblem.php?pid=5763 题意: 给出一个字符串和一个模式串,模式串有两种意思,问这句话有几种意思. 思路:因为肯定要去字符串去找模 ...

  2. nodejs中mysql断线重连

    之前写了个小程序Node News,用到了MySQL数据库,在本地测试均没神马问题.放上服务器运行一段时间后,偶然发现打开页面的时候页面一直处于等待状态,直到Nginx返回超时错误.于是上服务器检查了 ...

  3. shell脚本中case select 的使用

    #!/bin/bash # case echo "1.Install PHP" echo "2.Install Mysql" echo "3.Inst ...

  4. Nginx、Apache工作原理及Nginx为何比Apache高效

    Nginx才短短几年,就拿下了web服务器大笔江山,众所周知,Nginx在处理大并发静态请求方面,效率明显高于httpd,甚至能轻松解决C10K问题. 在高并发连接的情况下,Nginx是Apache服 ...

  5. 图片服务器(FastDFS)的搭建

    1.1 什么是FastDFS FastDFS是用c语言编写的一款开源的分布式文件系统.FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用.高性能等指标,使用Fa ...

  6. 由浅入深了解EventBus:(三)

    原理 EventBus的核心工作机制如下图 在EventBus3.0架构图: EventBus类 在EventBus3.0框架的内部,核心类就是EventBus,订阅者的注册/订阅,解除注册,以及事件 ...

  7. SQL HAVING用法详解

    来自:http://blog.csdn.net/wozeze1/article/details/6031318 HAVING 子句对 GROUP BY 子句设置条件的方式与 WHERE 和 SELEC ...

  8. 初次使用VCS仿真软件

    由于刚开始接触VCS,对于VCS不是太了解,在网上找了很多的资料终于遇到了一个相对比较初级的入门资料,这个资料是以一个简单的4位加法器为例来介绍vcs的用法的,比较好入门,这个文章的地址如下: htt ...

  9. 【这些年】Linux C/C++软件开发用过的工具

          这些年一直从事Linux下C/C++软件开发,学习工作中用到了不少开发工具,一直想做个总结,却总是因为这个原因那个原因,未能动笔.趁今天天气凉爽,空气清新,花点儿功夫,做一个小结啦,防止以 ...

  10. 创建python3.6的虚拟开发环境virtualenv

    为了保证各个项目环境和插件的相对独立,使用virtualenv作为python的虚拟运行环境,这样在项目维护方面,减少在依赖包上所花费的时间. 1.安装 virtualenv虚拟环境插件 pip in ...