字符串(tjoi2016,heoi2016,bzoj4556)(sam(后缀自动机)+线段树合并+倍增+二分答案)
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
一个长为\(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
题意:
中文题面,不解释
题解:
首先前缀不好处理,我们把字符串翻转。
然后发现很难直接求出答案,就采取二分最长后缀并验证。我们只需要验证左区间的后缀是否出现在右区间就行了。
然后用倍增在\(parent\)树上找到对应该子串的节点,并求出该节点能覆盖的节点(他子树中的节点),所以我们可以用线段树合并来求解。
#include<bits/stdc++.h>
#define mid (l+(r-l)/2)
using namespace std;
const int N=200010;
int lson[N*30],rson[N*30],sum[N*30];
int n,m,head[N],nxt[N],bian[N],p,q,np,nq,mp[N],pos[N],cnt,last;
int root[N],ch[N][27],fa[N],l[N],tot,sz,f[N][19],dep[N];
char s[N];
void ins(int x)
{
int c=s[x]-'a'+1;
p=last; np=++cnt; last=np; mp[np]=x; pos[x]=np;
l[np]=l[p]+1;
for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if (!p) fa[np]=1;
else {
q=ch[p][c];
if (l[p]+1==l[q]) fa[np]=q;
else {
nq=++cnt; l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void add(int x,int y){
tot++,bian[tot]=y,nxt[tot]=head[x],head[x]=tot;
}
int updata(int pre,int l,int r,int x){
int rt=++sz; sum[rt]=sum[pre]+1;
if (l==r) return rt;
lson[rt]=lson[pre];
rson[rt]=rson[pre];
if (x<=mid) lson[rt]=updata(lson[pre],l,mid,x);
else rson[rt]=updata(rson[pre],mid+1,r,x);
return rt;
}
void pushup(int x){
int l=lson[x],r=rson[x];
sum[x]=sum[l]+sum[r];
}
int merge(int x,int y){
if(!x||!y)return x+y;
int rt=++sz;
lson[rt]=merge(lson[x],lson[y]);
rson[rt]=merge(rson[x],rson[y]);
pushup(rt);
return rt;
}
void dfs(int x){
for (int i=1;i<=17;i++){
if (dep[x]-(1<<i)<0) break;
f[x][i]=f[f[x][i-1]][i-1];
}
for (int i=head[x];i;i=nxt[i]){
f[bian[i]][0]=x,dep[bian[i]]=dep[x]+1;
dfs(bian[i]);
root[x]=merge(root[x],root[bian[i]]);
}
}
int query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return sum[rt];
int ans=0;
if(x<=mid)ans+=query(lson[rt],l,mid,x,y);
if(y>mid)ans+=query(rson[rt],mid+1,r,x,y);
return ans;
}
bool check(int k,int left,int right,int x){
if(k==0)return 1;
for(int rt=17;rt>=0;rt--)
if(l[f[x][rt]]>=k)x=f[x][rt];
return query(root[x],1,n,left,right);
}
int main(){
cin>>n>>m;
cin>>s+1;reverse(s+1,s+n+1);
last=++cnt;
for(int i=1;i<=n;++i)ins(i);
for(int i=1;i<=cnt;++i)add(fa[i],i);
for(int i=1;i<=cnt;++i)
if(mp[i])root[i]=updata(root[i],1,n,mp[i]);
dep[1]=1; dfs(1);
for (int i=1;i<=m;++i){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
swap(a,b); swap(c,d);
a=n-a+1; b=n-b+1; c=n-c+1; d=n-d+1;
int l=0,r=min(d-c+1,b-a+1),ans=0;
while (l<=r){
if (check(mid,a+mid-1,b,pos[d])) ans=max(ans,mid),l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
}
字符串(tjoi2016,heoi2016,bzoj4556)(sam(后缀自动机)+线段树合并+倍增+二分答案)的更多相关文章
- HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)
题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点, ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 657 Solved: 274[Su ...
- [NOI2018]你的名字(后缀自动机+线段树合并)
看到题目名字去补番是种怎么样的体验 我只会 \(68\) 分,打了个暴力.正解看了一会儿,发现跟 \([HEOI2016/TJOI2016]\) 字符串很像,用线段树合并维护 \(endpos\) 集 ...
随机推荐
- .net 4.0的Lazy<T>方法,反射实现延迟加载。
//自己山寨.public class YaLazy<T> { private bool _isValueCreated = false; public bool IsValueCreat ...
- Nginx 分析access日志文件
Nginx Access Log日志统计分析常用命令 IP相关统计 统计IP访问量 awk '{print $1}' access.log | sort -n | uniq | wc -l 查看某一时 ...
- cout<<endl 本质探索
C++中,有一种对象叫操控器(manipulators),专门用来操控stream的对象,在C++标准中,预定义好几种操控器,常见的有: flush 刷新output缓冲区,将内容写入输出设备 end ...
- 05 Maven 生命周期和插件
Maven 生命周期和插件 除了坐标.依赖以及仓库之外, Maven 另外两个核心概念是生命周期和插件.在有关 Maven 的日常使用中,命令行的输入往往就对应了生命周期,如 mvn package ...
- 解决Axure发布分享预览的3个方法
公司的同事制作的一个产品原型,要发给我,我当时正在客户这里,电脑上并没有Axure,客户又催得急,感到一阵无奈.这次回来之后,经过一番摸索,发现还是有办法的.这里给大家分享一下Axure发布分享预览的 ...
- 数据帮助类DBhelper的定义
数据库帮助类的使用DBhelperusing System;using System.Collections.Generic;using System.Text;using System.Data;u ...
- maven web 项目 打入 jar 包 , 和编译入 java 文件到 web-inf 下
<outputDirectory>src\main\webapp\WEB-INF\classes</outputDirectory> 可以把 类文件编译到 web-inf 下 ...
- PacBio下机数据如何看?
一开始拿到三代测序的下机数据时,蒙了,readme ?三代测序的下机数据都有哪些,以及他们具体的格式是怎么样的(以sequel 平台为主). 测序过程 SMRTbell A adapter通用接头,两 ...
- NET(C#)连接各类数据库-集锦
1.C#连接连接Access程序代码:------------------------------------------------------------------------------- u ...
- MySQL之安装以及辅助工具的安装
一 下载地址 MySQL 下载地址: http://rj.baidu.com/soft/detail/12585.html?ald 客户端工具:MavicatforMySQL 绿色版下载地址:http ...