【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分
4556: [Tjoi2016&Heoi2016]字符串
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 657 Solved: 274
[Submit][Status][Discuss]
Description
Input
Output
对于每一次询问,输出答案。
Sample Input
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4
Sample Output
1
2
2
2
HINT
Source
Solution
做过类似的题就会很好做了。
考虑后缀数组的做法,就是二分一个答案mid,那么在[a,b]中答案子串的起点一定只能出现在[a,b-mid+1],那么只需要判定和Suffix(c)的LCP>=mid的子串是否有[a,b-mid+1]中的即可。
然后考虑LCP在Height数组上从Suffix(c)向左右单调不增的,所以可以二分出满足与Suffix(c)的LCP>=mid的区间[L,R],然后利用主席树去查是否有[a,b-mid+1]中的。
同样可以利用后缀自动机做,因为是公共前缀,所以可以考虑把串翻转转化成后缀,利用线段树合并预处理出每个节点的状态,同理二分答案,利用预处理的查询即可。
后缀自动机的做法常数较小,实际跑起来效果明显优于后缀数组做法。
Code
后缀数组
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
#define MAXN 100010 int N,M;
char S[MAXN]; int R[MAXN],SA[MAXN],height[MAXN],rank[MAXN],t1[MAXN],t2[MAXN],st[MAXN];
inline void Sort(int *x,int *y,int *sa,int L,int M)
{
for (int i=0; i<=M; i++) st[i]=0;
for (int i=0; i<L; i++) st[x[y[i]]]++;
for (int i=1; i<=M; i++) st[i]+=st[i-1];
for (int i=L-1; i>=0; i--) sa[--st[x[y[i]]]]=y[i];
} inline void DA(int *r,int *sa,int L,int M)
{
int *x=t1,*y=t2,*t,i,j,p;
for (int i=0; i<L; i++) x[i]=r[i],y[i]=i;
Sort(x,y,sa,L,M);
for (j=1,p=1; j<L && p<L; j<<=1,M=p-1)
{
for (p=0,i=L-j; i<L; i++) y[p++]=i;
for (i=0; i<L; i++) if (sa[i]>=j) y[p++]=SA[i]-j;
Sort(x,y,sa,L,M);
for (t=x,x=y,y=t,i=1,x[sa[0]]=0,p=1; i<L; i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]? p-1:p++;
}
} inline void Height(int *r,int *sa,int *rank,int *h,int L)
{
h[1]=0;
for (int i=1; i<=L; i++) rank[sa[i]]=i;
for (int i=1,k=0,j; i<=L; h[rank[i++]]=k)
for (k? --k:k=0,j=sa[rank[i]-1]; r[j+k]==r[i+k]; k++);
} int log_2[MAXN],dp[MAXN][21];
inline void St(int L)
{
log_2[0]=-1;
for (int i=1; i<=L; i++)
if (i&(i-1))
log_2[i]=log_2[i-1];
else
log_2[i]=log_2[i-1]+1;
for (int i=0; i<=L; i++) dp[i][0]=height[i+1];
for (int j=1; (1<<j)<=L; j++)
for (int i=0; i+(1<<j)-1<=L; i++)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
} inline int RMQ(int l,int r) {if (l==r) return N-SA[l]; int k=log_2[r-l]; return min(dp[l][k],dp[r-(1<<k)][k]);}
inline int LCP(int l,int r) {if (l>r) swap(l,r); return RMQ(l,r);} int sz,sum[MAXN*20],lson[MAXN*20],rson[MAXN*20],root[MAXN];
inline void Insert(int &x,int y,int l,int r,int pos)
{
x=++sz; sum[x]=sum[y]+1;
if (l==r) return;
lson[x]=lson[y]; rson[x]=rson[y];
int mid=(l+r)>>1;
if (pos<=mid) Insert(lson[x],lson[y],l,mid,pos);
else Insert(rson[x],rson[y],mid+1,r,pos);
} inline int Query(int x,int y,int l,int r,int L,int R)
{
if (L<=l && R>=r) return sum[y]-sum[x];
int mid=(l+r)>>1,re=0;
if (L<=mid) re+=Query(lson[x],lson[y],l,mid,L,R);
if (R>mid) re+=Query(rson[x],rson[y],mid+1,r,L,R);
return re;
} inline int GetL(int x,int y)
{
int l=1,r=rank[x],re=-1;
while (l<=r) {
int mid=(l+r)>>1;
if (RMQ(mid,rank[x])>=y) r=mid-1,re=mid;
else l=mid+1;
}
return re;
} inline int GetR(int x,int y)
{
int l=rank[x],r=N,re=-1;
while (l<=r) {
int mid=(l+r)>>1;
if (RMQ(rank[x],mid)>=y) l=mid+1,re=mid;
else r=mid-1;
}
return re;
} int main()
{
N=read(),M=read();
scanf("%s",S+1);
for (int i=1; i<=N; i++) R[i]=S[i]-'a'+1;
DA(R,SA,N+1,28); Height(R,SA,rank,height,N); St(N); // for (int i=1; i<=N; i++) printf("%d ",SA[i]); puts("");
// for (int i=1; i<=N; i++) printf("%d ",rank[i]); puts(""); for (int i=1; i<=N; i++) Insert(root[i],root[i-1],1,N,SA[i]);
while (M--) {
int a=read(),b=read(),c=read(),d=read();
int l=1,r=min(b-a+1,d-c+1),ans=0,L,R;
while (l<=r) {
int mid=(l+r)>>1;
L=GetL(c,mid); R=GetR(c,mid);
if (L==-1) L=rank[c];
if (R==-1) R=rank[c];
if (Query(root[L-1],root[R],1,N,a,b-mid+1)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}
后缀自动机
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
return x*f;
} #define MAXN 100010 int N,M;
char S[MAXN]; struct SgtNode{
int lson,rson;
}tree[MAXN*40];
int cnt,root[MAXN<<1];
inline void Insert(int &x,int l,int r,int pos)
{
x=++cnt;
if (l==r) return;
int mid=(l+r)>>1;
if (pos<=mid) Insert(tree[x].lson,l,mid,pos);
else Insert(tree[x].rson,mid+1,r,pos);
} inline int Merge(int x,int y)
{
if (!x || !y) return x|y;
int z=++cnt;
if (x==y) return x;
tree[z].lson=Merge(tree[x].lson,tree[y].lson);
tree[z].rson=Merge(tree[x].rson,tree[y].rson);
return z;
} inline int Query(int x,int l,int r,int L,int R)
{
if (!x) return 0;
if (L<=l && R>=r) return 1;
int mid=(l+r)>>1,re=0;
if (L<=mid) re|=Query(tree[x].lson,l,mid,L,R);
if (R>mid) re|=Query(tree[x].rson,mid+1,r,L,R);
return re;
} int len[MAXN<<1],son[MAXN<<1][26],par[MAXN<<1],st[MAXN],id[MAXN<<1],sz=1,rt=1,last=1,father[21][MAXN<<1];
inline void Extend(int c)
{
int cur=++sz,p=last;
len[cur]=len[p]+1;
while (p && !son[p][c]) son[p][c]=cur,p=par[p];
if (!p) par[cur]=rt;
else {
int q=son[p][c];
if (len[p]+1==len[q]) par[cur]=q;
else {
int nq=++sz;
memcpy(son[nq],son[q],sizeof(son[nq]));
len[nq]=len[p]+1; par[nq]=par[q];
while (p && son[p][c]==q) son[p][c]=nq,p=par[p];
par[q]=par[cur]=nq;
}
}
last=cur;
} inline void Sort()
{
for (int i=0; i<=N; i++) st[i]=0;
for (int i=1; i<=sz; i++) st[len[i]]++;
for (int i=1; i<=N; i++) st[i]+=st[i-1];
for (int i=1; i<=sz; i++) id[st[len[i]]--]=i;
for (int i=sz; i>=1; i--) {
int x=id[i];
root[par[x]]=Merge(root[par[x]],root[x]);
}
for (int i=1; i<=sz; i++) {
int x=id[i];
father[0][x]=par[x];
for (int j=1; j<=20; j++)
father[j][x]=father[j-1][father[j-1][x]];
}
} inline bool Check(int x,int mid,int a,int b)
{
for (int i=20; i>=0; i--)
if (len[father[i][x]]>=mid) x=father[i][x];
return Query(root[x],1,N,a,b);
} int pos[MAXN];
int main()
{
N=read(),M=read();
scanf("%s",S+1); reverse(S+1,S+N+1);
for (int i=1; i<=N; i++) Extend(S[i]-'a'),pos[i]=last,Insert(root[last],1,N,i); Sort(); while (M--) {
int a=read(),b=read(),c=read(),d=read();
a=N-a+1,b=N-b+1,c=N-c+1,d=N-d+1; swap(a,b); swap(c,d);
int l=1,r=min(d-c+1,b-a+1),ans=0;
while (l<=r) {
int mid=(l+r)>>1;
if (Check(pos[d],mid,a+mid-1,b)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
} return 0;
}
【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分的更多相关文章
- 【BZOJ4556】字符串(后缀数组,主席树)
[BZOJ4556]字符串(后缀数组,主席树) 题面 BZOJ 题解 注意看题: 要求的是\([a,b]\)的子串和[c,d]的\(lcp\)的最大值 先来一下暴力吧 求出\(SA\)之后 暴力枚举\ ...
- 【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)
[BZOJ5304][HAOI2018]字串覆盖(后缀数组,主席树,倍增) 题面 BZOJ 洛谷 题解 贪心的想法是从左往右,能选就选.这个显然是正确的. 题目的数据范围很好的说明了要对于询问分开进行 ...
- HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)
题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点, ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex
题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- BZOJ 3626 [LNOI2014]LCA 树剖+(离线+线段树 // 在线+主席树)
BZOJ 4012 [HNOI2015]开店 的弱化版,离线了,而且没有边权(长度). 两种做法 1 树剖+离线+线段树 这道题求的是一个点zzz与[l,r][l,r][l,r]内所有点的lcalca ...
随机推荐
- vue 获取后端数据打印结果undefined问题
今天做项目时后端有一个要展示到页面上的附件需要前端获取,我获取到了那个附件的信息,但打印fj.name或fj.url时控制台就会显示undefined,后来才发现是json数据没有解析对,应该使用JS ...
- SQL记录-PLSQL游标
PL/SQL游标 Oracle会创建一个存储区域,被称为上下文区域,用于处理SQL语句,其中包含需要处理的语句,例如所有的信息,行数处理,等等. 游标是指向这一上下文的区域. PL/SQL通过控制光标 ...
- mysql学习------二进制日志
一.什么是二进制日志 1.记录对数据发生或潜在发生更改的sql语句 2.二进制格式保存 3.用途广泛,包括 a.查看数据库变更历史 b.数据库增量备份 c.数据库灾难恢复 d.mysql replic ...
- Linux USB Host-Controller的初始化代码框架分析【转】
转自:http://blog.csdn.net/zkami/article/details/2496770 usb_hcd_omap_probe (const struct hc_driver *dr ...
- 使用pt-table-checksum校验MySQL主从复制【转】
pt-table-checksum是一个基于MySQL数据库主从架构在线数据一致性校验工具.其工作原理在主库上运行, 通过对同步的表在主从段执行checksum, 从而判断数据是否一致.在校验完毕时, ...
- 查看nginx | apache | php | tengine | tomcat版本的信息以及如何隐藏版本信息【转】
转自: 查看nginx | apache | php | tengine | tomcat版本的信息以及如何隐藏版本信息 - 追马 - 51CTO技术博客http://lovelace.blog.51 ...
- %08lx
u-boot中代码如下: debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr); 对应设备上的打印消息如下: N ...
- apache显示目录文件列表
在apache服务器下访问一个目录,如果没有index.html/index.php,则会报错. 为了访问文件夹: 1. 在 /var/www/html 目录下新建 /d/ mkdir d 2. t ...
- Kaggle大数据竞赛平台入门
Kaggle大数据竞赛平台入门 大数据竞赛平台,国内主要是天池大数据竞赛和DataCastle,国外主要就是Kaggle.Kaggle是一个数据挖掘的竞赛平台,网站为:https://www.kagg ...
- java 异常的限制
一. 1.) 在覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常 2.) 在基类构造器声明的异常,在子类必须抛出,子类的构造器可以抛出任何异常,但是必须抛出基类构造器的异常 3.) 在基类 ...