[HEOI2016] 字符串 - 后缀数组,主席树,ST表,二分
[HEOI2016] 字符串
Description
给定一个字符串 \(S\), 有 \(m\) 个询问,每个询问给定参数 \((a,b,c,d)\) ,求 \(s[a..b]\) 的子串与 \(s[c..d]\) 的最长公共前缀长度的最大值。
Solution
读懂题意以后就很简单。把后缀数组和高度数组都搞出来,并对高度数组建立 ST 表,然后对于每个询问找到 \(s[c..d]\) 在后缀排序中的位置,二分一个 \(LCP\) 长度,检验只需要查询某一段下标区间内有没有 \(rank\) 在某个区间内的值,对 \(rank\) 数组建一个主席树即可。
我太菜了,写了一个多小时,瞎优化主席树才卡过了常。正思索着也没哪里常数太大,后来发现原来是 \(log\) 的锅……
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int fastlog[N];
namespace st
{
int a[N][21];
void build(int *src,int n)
{
for(int i=1; i<=n; i++)
a[i][0]=src[i];
for(int i=1; i<=20; i++)
for(int j=1; j<=n-(1<<i)+1; j++)
a[j][i]=min(a[j][i-1],a[j+(1<<(i-1))][i-1]);
}
int query(int l,int r)
{
int j=fastlog[r-l+1];
return min(a[l][j],a[r-(1<<j)+1][j]);
}
}
namespace sa
{
int n,m=256,sa[N],y[N],u[N],v[N],o[N],r[N],h[N],T;
// sa: Suffix Array
// r: Rank Array
// h: Height Array (between sa[i] & sa[i-1])
char str[N];
void solve()
{
memset(sa,0,sizeof sa);
memset(y,0,sizeof y);
memset(u,0,sizeof u);
memset(v,0,sizeof v);
memset(o,0,sizeof o);
memset(r,0,sizeof r);
memset(h,0,sizeof h);
n=strlen(str+1);
for(int i=1; i<=n; i++)
u[str[i]]++;
for(int i=1; i<=m; i++)
u[i]+=u[i-1];
for(int i=n; i>=1; i--)
sa[u[str[i]]--]=i;
r[sa[1]]=1;
for(int i=2; i<=n; i++)
r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
for(int l=1; r[sa[n]]<n; l<<=1)
{
memset(u,0,sizeof u);
memset(v,0,sizeof v);
memcpy(o,r,sizeof r);
for(int i=1; i<=n; i++)
u[r[i]]++, v[r[i+l]]++;
for(int i=1; i<=n; i++)
u[i]+=u[i-1], v[i]+=v[i-1];
for(int i=n; i>=1; i--)
y[v[r[i+l]]--]=i;
for(int i=n; i>=1; i--)
sa[u[r[y[i]]]--]=y[i];
r[sa[1]]=1;
for(int i=2; i<=n; i++)
r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
}
{
int i,j,k=0;
for(int i=1; i<=n; h[r[i++]]=k)
for(k?k--:0,j=sa[r[i]-1]; str[i+k]==str[j+k]; k++);
}
}
}
namespace seg
{
int n,m,t1,t2,t3,k,a[N],b[N],c[25*N],d[25*N],e[25*N],f,g[N],q[N],s,h[N]= {1,0};
int build(int l,int r)
{
int p=++f;
if(r==l)
c[p]=0;
else
d[p]=build(l,(l+r)/2), e[p]=build((l+r)/2+1,r);
return p;
}
void pushup(int p)
{
c[p]=c[d[p]]+c[e[p]];
}
int modify(int j,int l,int r,int k)
{
int p=++f;
c[p]=c[j];
d[p]=d[j];
e[p]=e[j];
if(l==r)
{
c[p]++;
return p;
}
if(k<=(l+r)/2)
d[p]=modify(d[j],l,(l+r)/2,k);
else
e[p]=modify(e[j],(l+r)/2+1,r,k);
pushup(p);
return p;
}
bool query(int i,int j,int l,int r,int ql,int qr)
{
if(l>qr||r<ql)
return 0;
if(l>=ql&&r<=qr)
return (c[j]-c[i]?1:0);
if(query(d[i],d[j],l,(l+r)/2,ql,qr)||query(e[i],e[j],(l+r)/2+1,r,ql,qr))
return 1;
else
return 0;
}
bool query(int i,int j,int ql,int qr)
{
return query(g[i-1],g[j],1,n,ql,qr);
}
void presolve(int *src,int _n)
{
n=_n;
g[0]=build(1,n);
for(int i=1; i<=n; i++)
g[i]=modify(g[i-1],1,n,src[i]);
}
}
int n,m;
int lcp(int p,int q)
{
if(p>q)
swap(p,q);
if(p==q)
return n-sa::sa[p]+1;
else
return st::query(p+1,q);
}
int findLeftBound(int lim,int pos,int val)
{
int l=lim,r=pos+1;
while(r>l)
{
int mid=(l+r)/2;
if(lcp(mid,pos)>=val)
r=mid;
else
l=mid+1;
}
if(lcp(l,pos)>=val)
return l;
else
return 0;
}
int findRightBound(int lim,int pos,int val)
{
int l=pos+1,r=lim+1;
while(r>l)
{
int mid=(l+r)/2;
if(lcp(mid,pos)>=val)
l=mid+1;
else
r=mid;
}
if(lcp(l-1,pos)>=val)
return l-1;
else
return 0;
}
int main()
{
ios::sync_with_stdio(false);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fastlog[i]=log2(i);
scanf("%s",sa::str+1);
sa::solve();
st::build(sa::h,n);
seg::presolve(sa::r,n);
for(int i=1; i<=m; i++)
{
int t1,t2,t3,t4;
scanf("%d%d%d%d",&t1,&t2,&t3,&t4);
int l=1,r=min(t2-t1,t4-t3)+2,ax=0;
while(r>l)
{
int mid=(l+r)/2;
int lb=findLeftBound(1,sa::r[t3],mid);
int rb=findRightBound(n,sa::r[t3],mid);
if(seg::query(t1,t2-mid+1,lb,rb))
{
ax=mid;
l=mid+1;
}
else
{
r=mid;
}
}
printf("%d\n",ax);
}
}
[HEOI2016] 字符串 - 后缀数组,主席树,ST表,二分的更多相关文章
- BZOJ4556:[TJOI\HEOI2016]字符串(后缀数组,主席树,二分,ST表)
Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱 ...
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MB Description 佳媛姐姐过生日的时候,她的小 ...
- bzoj 4556 [Tjoi2016&Heoi2016]字符串——后缀数组+主席树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4556 本来只要查 ht[ ] 数组上的前驱和后继就行,但有长度的限制.可以二分答案解决!然后 ...
- BZOJ3473:字符串(后缀数组,主席树,二分,ST表)
Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ...
- P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案
$ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...
- luoguP5108 仰望半月的夜空 [官方?]题解 后缀数组 / 后缀树 / 后缀自动机 + 线段树 / st表 + 二分
仰望半月的夜空 题解 可以的话,支持一下原作吧... 这道题数据很弱..... 因此各种乱搞估计都是能过的.... 算法一 暴力长度然后判断判断,复杂度\(O(n^3)\) 期望得分15分 算法二 通 ...
- bzoj 4556 字符串 —— 后缀数组+主席树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4556 就是找一个 rk 在一段区间内的前驱和后继: 由于 LCP 还有区间长度的限制,所以可 ...
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案
Solution 1: 后缀数组暴力大法好 #include <map> #include <cmath> #include <queue> #include &l ...
- HDU-6704 K-th occurrence(后缀数组+主席树)
题意 给一个长度为n的字符串,Q次询问,每次询问\((l,r,k)\) , 回答子串\(s_ls_{l+1}\cdots s_r\) 第\(k\) 次出现的位置,若不存在输出-1.\(n\le 1e5 ...
随机推荐
- C# WPF 时钟动画(2/2)
模拟实现时钟效果,学习WPF动画好例子,本文承接上文 C# WPF 时钟动画(1/2). 微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. C# ...
- js遇到代码出现问题时如何调试代码
单步跟踪调试 debugger; 控制台watch功能查看变量当前值 进入函数操作 随着不断点击,不停进行循环,指定变量的值也在发生改变 添加断点 跳入跳出函数 throw new Error() 主 ...
- JAVA控制流程
Java代码有三种执行结构流程,顺序结构.分支结构.循环结构 顺序结构 顺序结构是最简单的代码执行结构,从代码开始逐步执行每一句代码到结束 public class C { public static ...
- Pikachu-Unsafe Filedownload(不安全的文件下载)
不安全的文件下载概述 文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,便会向后台发送一个下载请求,一般这个请求会包含一个需要下载的文件名称,后台在收到请求后 会开始执行下载代码,将该文 ...
- SpringBoot学习- 11、更好用的代码生成工具EasyCode
SpringBoot学习足迹 之前的mybatis代码生成工具无法自定义模板,找了一个可以自定义模板的插件,初学者最好用比较齐全的代码生成工具,不然拼错一个代码会掉坑里半天爬不出来. 有的同学会说干么 ...
- Easyui-Tree和Combotree使用注意事项-sunziren
版权声明:本文为sunziren原创文章,博客园首发,转载务必注明出处以及作者名称. Easyui-Tree和Combotree所使用的数据结构是类似的,在我的上一篇文章<Easyui-Tree ...
- nginx模块之ngx_http_upstream_module
ngx_http_upstream_module 示例: http上下文: upstream upservers{ ip_hash; //根据客户端IP进行调度,每个客户端ip地址访问时每个ip生成一 ...
- oracle基础知识点
一.count(*).count(1).count(字段名)的区别select count(*) from t_md_inst --153797 --包含字段为null 的记录select count ...
- PAT (Basic Level) Practice (中文)1041 考试座位号 (15 分)
每个 PAT 考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位.正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码,考试时考生需要换到考 ...
- nginx 配置 强制访问https
使用nginx的301状态码 server { listen ; if ($scheme = 'http') { return 301 https://$server_name$requ ...