【BZOJ4556】字符串(后缀数组,主席树)

题面

BZOJ

题解

注意看题:

要求的是\([a,b]\)的子串和[c,d]的\(lcp\)的最大值


先来一下暴力吧

求出\(SA\)之后

暴力枚举\([A,B]\)之间的后缀

求一个\(lcp\)

复杂度\(O(nm)\)

\(40\)分到手

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int lg[MAX],a[MAX];
char s[MAX];
int n,m;
struct SA
{
int x[MAX],y[MAX],SA[MAX],t[MAX];
int ht[MAX],rk[MAX],p[20][MAX];
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void GetSA()
{
int m=50;
for(int i=1;i<=n;++i)t[x[i]=a[i]]++;
for(int i=1;i<=m;++i)t[i]+=t[i-1];
for(int i=n;i;--i)SA[t[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(int i=n-k+1;i<=n;++i)y[++p]=i;
for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;
for(int i=0;i<=m;++i)t[i]=0;
for(int i=1;i<=n;++i)t[x[y[i]]]++;
for(int i=1;i<=m;++i)t[i]+=t[i-1];
for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];
swap(x,y);
x[SA[1]]=p=1;
for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
if(p>=n)break;
m=p;
}
for(int i=1;i<=n;++i)rk[SA[i]]=i;
for(int i=1,j=0;i<=n;++i)
{
if(j)--j;
while(a[i+j]==a[SA[rk[i]-1]+j])++j;
ht[rk[i]]=j;
}
}
void prepare()
{
for(int i=1;i<=n;++i)p[0][i]=ht[i];
for(int j=1;j<17;++j)
for(int i=1;i<=n;++i)
p[j][i]=min(p[j-1][i],p[j-1][i+(1<<(j-1))]);
}
int rmq(int l,int r){return min(p[lg[r-l+1]][l],p[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);}
int lcp(int l,int r)
{
if(l==r)return n-l+1;
if(rk[l]>rk[r])swap(l,r);
return rmq(rk[l]+1,rk[r]);
}
}SA; int main()
{
n=read();m=read();
scanf("%s",s+1);
for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;++i)a[i]=s[i]-96;
SA.GetSA();
SA.prepare();
while(m--)
{
int A=read(),B=read(),C=read(),D=read();
int ans=0;
for(int i=A;i<=B;++i)ans=max(ans,min(min(D-C+1,B-i+1),SA.lcp(i,C)));
printf("%d\n",ans);
}
return 0;
}

我是真的垃圾

不会做

确定了\(C\)之后

二分一个答案\(mid\)

那么,可以确定必须要在一个区间内存在\([A,B]\)中某个位置的\(rank\)

那么,二分出满足\(lcp\)大于\(mid\)的区间

然后查询是否满足条件即可

查询使用主席树来做

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int lg[MAX],a[MAX];
char s[MAX];
int n,m;
struct President_SegMent_Tree
{
struct Node
{
int ls,rs;
int v;
}t[MAX<<5];
int tot,rt[MAX];
void Modify(int &now,int ff,int l,int r,int p,int w)
{
now=++tot;t[now]=t[ff];t[now].v+=w;
if(l==r)return;
int mid=(l+r)>>1;
if(p<=mid)Modify(t[now].ls,t[ff].ls,l,mid,p,w);
else Modify(t[now].rs,t[ff].rs,mid+1,r,p,w);
}
int Query(int r1,int r2,int l,int r,int L,int R)
{
if(L<=l&&r<=R){return t[r2].v-t[r1].v;}
int mid=(l+r)>>1,ret=0;
if(L<=mid)ret+=Query(t[r1].ls,t[r2].ls,l,mid,L,R);
if(R>mid)ret+=Query(t[r1].rs,t[r2].rs,mid+1,r,L,R);
return ret;
}
}seg;
struct SA
{
int x[MAX],y[MAX],SA[MAX],t[MAX];
int ht[MAX],rk[MAX],p[20][MAX];
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void GetSA()
{
int m=50;
for(int i=1;i<=n;++i)t[x[i]=a[i]]++;
for(int i=1;i<=m;++i)t[i]+=t[i-1];
for(int i=n;i;--i)SA[t[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(int i=n-k+1;i<=n;++i)y[++p]=i;
for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;
for(int i=0;i<=m;++i)t[i]=0;
for(int i=1;i<=n;++i)t[x[y[i]]]++;
for(int i=1;i<=m;++i)t[i]+=t[i-1];
for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];
swap(x,y);
x[SA[1]]=p=1;
for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
if(p>=n)break;
m=p;
}
for(int i=1;i<=n;++i)rk[SA[i]]=i;
for(int i=1,j=0;i<=n;++i)
{
if(j)--j;
while(a[i+j]==a[SA[rk[i]-1]+j])++j;
ht[rk[i]]=j;
}
}
void prepare()
{
for(int i=1;i<=n;++i)p[0][i]=ht[i];
for(int j=1;j<19;++j)
for(int i=1;i<=n;++i)
p[j][i]=min(p[j-1][i],p[j-1][i+(1<<(j-1))]);
}
int rmq(int l,int r)
{
if(l==r)return 1e9;++l;
return min(p[lg[r-l+1]][l],p[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);
}
bool check(int len,int A,int B,int C,int D)
{
int l,r,L=rk[C],R=rk[C];
l=1,r=rk[C];
while(l<r)
{
int mid=(l+r)>>1;
if(rmq(mid,rk[C])>=len)r=mid;
else l=mid+1;
}
L=l;
l=rk[C],r=n;
while(l<r)
{
int mid=(l+r+1)>>1;
if(rmq(rk[C],mid)>=len)l=mid;
else r=mid-1;
}
R=l;
return seg.Query(seg.rt[L-1],seg.rt[R],1,n,A,B-len+1);
}
}SA;
int main()
{
n=read();m=read();
scanf("%s",s+1);
for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;++i)a[i]=s[i]-96;
SA.GetSA();SA.prepare();
for(int i=1;i<=n;++i)seg.Modify(seg.rt[i],seg.rt[i-1],1,n,SA.SA[i],1);
while(m--)
{
int A=read(),B=read(),C=read(),D=read();
int ans=0;
int l=0,r=min(B-A,D-C)+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(SA.check(mid,A,B,C,D))ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}

【BZOJ4556】字符串(后缀数组,主席树)的更多相关文章

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

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

  2. BZOJ4556:[TJOI\HEOI2016]字符串(后缀数组,主席树,二分,ST表)

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

  3. BZOJ3473:字符串(后缀数组,主席树,二分,ST表)

    Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ...

  4. [HEOI2016] 字符串 - 后缀数组,主席树,ST表,二分

    [HEOI2016] 字符串 Description 给定一个字符串 \(S\), 有 \(m\) 个询问,每个询问给定参数 \((a,b,c,d)\) ,求 \(s[a..b]\) 的子串与 \(s ...

  5. bzoj 4556 [Tjoi2016&Heoi2016]字符串——后缀数组+主席树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4556 本来只要查 ht[ ] 数组上的前驱和后继就行,但有长度的限制.可以二分答案解决!然后 ...

  6. P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案

    $ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...

  7. bzoj 4556 字符串 —— 后缀数组+主席树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4556 就是找一个 rk 在一段区间内的前驱和后继: 由于 LCP 还有区间长度的限制,所以可 ...

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

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

  9. BZOJ 5496: [2019省队联测]字符串问题 (后缀数组+主席树优化建图+拓扑排序)

    题意 略 分析 考场上写了暴力建图40分溜了-(结果只得了30分) 然后只要优化建边就行了 首先给出的支配关系无法优化,就直接A向它支配的B连边. 考虑B向以B作为前缀的所有A连边,做一遍后缀数组,两 ...

  10. HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

    题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点, ...

随机推荐

  1. css的浮动与定位

    显示与隐藏 标签 属性 值 效果 区别 css的style display none 元素不可见 不占页面空间 css的style visibility hidden 元素不可见 占页面空间 disp ...

  2. Centos7安装GitLab

    GitLab CE Download Archives gitlab安装调试小记 Gitlab Free Trial GitLab搭建手记 Gitlab社区版的使用 GUI PNG Gitlab升级到 ...

  3. Scoping the Project for iOS 7

    Scoping the Project On This Page Things Every App Must Do Things Every App Should Do If You Must Con ...

  4. 关于 iOS 分类(Category)的实现代码

    其实质是对一个类的扩充,对类扩充的方法有两种: (1)通过继承(经常用到) (2)通过分类 一个已知类Name 其头文件Name.h #import <Foundation/Foundation ...

  5. 关于echarts 报错 初始化对象未定义

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  6. SDK编程之多线程编程

    本课中,我们将学习如何进行多线程编程.另外我们还将学习如何在不同的线程间进行通信. 理论:前一课中,我们学习了进程,其中讲到每一个进程至少要有一个主线程.这个线程其实是进程执行的一条线索,除此主线程外 ...

  7. mysql存储引擎、事务

    MySQL存储引擎介绍 文件系统 操作系统组织和存取数据的一种机制. 文件系统是一种软件. 文件系统类型 ext2  ext3  ext4  xfs 数据 不管使用什么文件系统,数据内容不会变化 不同 ...

  8. Jupyter 初体验

    简介 Jupyter notebook 是一个非常强大的工具,可以创建漂亮的交互式文档. 安装 安装环境:win7专业版32位系统 + python 3.6.4 安装命令:pip install ju ...

  9. 老男孩Python全栈开发(92天全)视频教程 自学笔记18

    day18课程内容: os模块 import osprint(os.getcwd())#D:\untitled\练习题 获取当前工作目录os.chdir(r'D:\untitled\练习题\16.1切 ...

  10. 《android开发艺术探索》读书笔记(五)--RemoteViews

    接上篇<android开发艺术探索>读书笔记(四)--View工作原理 No1: RemoteViews使用场景:通知栏和桌面小部件 No2: 通知栏主要通过NotificationMan ...