NKOJ 4151 (TJOI 2016&HEOI 2016)字符串(后缀数组+倍增+主席树)
来源:互联网 发布:淘宝的保证金怎么退 编辑:程序博客网 时间:2024/06/08 16:30
【Tjoi2016&Heoi2016】字符串
问题描述
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?
输入格式
输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。
输出格式
对于每一次询问,输出答案。
样例输入
5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4
样例输出
1
1
2
2
2
提示
1<=n,m<=100,000,字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n
此题思路比较直接,考虑到直接搞的麻烦,考虑二分答案,那么问题可以转化成求
考虑如何判断,考虑SA数组,显然与c开头的后缀最长公共前缀大于
关于找出区间,RMQ处理Height数组,然后倍增查找即可。
代码:
#include<stdio.h>#include<iostream>#include<algorithm>#include<cstring>#define N 200005#define M 10000005using namespace std;char s[N];int n,m,SA[N],Rank[N],H[N],F[N][20],S=18;int wa[N],wb[N],T[N];int tot,ls[M],rs[M],v[M],rt[N];bool equ(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}void GSA(char *r,int *sa,int a,int b){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<b;i++)T[i]=0; for(i=0;i<a;i++)T[x[i]=r[i]]++; for(i=1;i<b;i++)T[i]+=T[i-1]; for(i=a-1;i>=0;i--)sa[--T[x[i]]]=i; for(p=1,j=1;p<a;j<<=1,b=p) { for(p=0,i=a-j;i<a;i++)y[p++]=i; for(i=0;i<a;i++)if(sa[i]>=j)y[p++]=sa[i]-j; for(i=0;i<b;i++)T[i]=0; for(i=0;i<a;i++)T[x[y[i]]]++; for(i=1;i<b;i++)T[i]+=T[i-1]; for(i=a-1;i>=0;i--)sa[--T[x[y[i]]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<a;i++) x[sa[i]]=equ(y,sa[i-1],sa[i],j)?p-1:p++; }}void GH(char *r,int *sa,int a){ int i,j,k=0; for(i=1;i<=a;i++)Rank[sa[i]]=i; for(i=0;i<a;H[Rank[i++]]=k) for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++);}void RMQ(){ int i,j; for(i=1;i<=n;i++)F[i][0]=H[i]; for(i=n;i>=1;i--) for(j=1;i+(1<<j-1)<=n;j++) F[i][j]=min(F[i][j-1],F[i+(1<<j-1)][j-1]);}int CP(int x){ int p=++tot; ls[p]=ls[x]; rs[p]=rs[x]; v[p]=v[x]; return p;}int ADD(int p,int l,int r,int k){ int o=CP(p); if(l==r){v[o]++;return o;} int mid=l+r>>1; if(k<=mid)ls[o]=ADD(ls[o],l,mid,k); else rs[o]=ADD(rs[o],mid+1,r,k); v[o]=v[ls[o]]+v[rs[o]]; return o;}int GS(int lp,int rp,int l,int r,int x,int y){ if(x<=l&&y>=r)return v[rp]-v[lp]; int mid=l+r>>1,cs=0; if(x<=mid&&y>=l)cs+=GS(ls[lp],ls[rp],l,mid,x,y); if(x<=r&&y>mid)cs+=GS(rs[lp],rs[rp],mid+1,r,x,y); return cs;}bool ok(int t,int a,int b,int c){ int l=Rank[c],r=Rank[c]; for(int i=S;i>=0;i--) { if(l-(1<<i)>=0&&F[l-(1<<i)+1][i]>=t)l=l-(1<<i); if(r+(1<<i)<=n&&F[r+1][i]>=t)r=r+(1<<i); } return GS(rt[a-1],rt[b],1,n,l,r);}void EF(int l,int r,int a,int b,int c){ int mid; while(l<=r) { mid=l+r>>1; if(ok(mid,a,b-mid+1,c))l=mid+1; else r=mid-1; } printf("%d\n",r);}int main(){ int a,b,c,d; scanf("%d%d%s",&n,&m,s);s[n]='a'-1; GSA(s,SA,n+1,200);GH(s,SA,n);RMQ(); rt[0]=ADD(rt[0],1,n,Rank[0]); for(int i=1;i<n;i++)rt[i]=ADD(rt[i-1],1,n,Rank[i]); while(m--) { scanf("%d%d%d%d",&a,&b,&c,&d); a--;b--;c--;d--; EF(1,min(b-a+1,d-c+1),a,b,c); }}
- NKOJ 4151 (TJOI 2016&HEOI 2016)字符串(后缀数组+倍增+主席树)
- 【TJOI & HEOI 2016】【JZOJ 4604】【BZOJ 4551】 树
- NKOJ 2182 (HEOI 2012) 采花(树状数组/线段树)
- 后缀数组(倍增)
- 后缀数组(倍增)
- COGS-2275 [HEOI 2016] seq(树状数组+线段树)
- NKOJ 2844 (APIO 2014)回文串(Manacher+后缀自动机+倍增/回文树)
- 后缀数组(倍增法)
- 【TJOI & HEOI 2016】【JZOJ 4605】 【BZOJ 4552】排序
- 【TJOI & HEOI 2016】【BZOJ 4554】【JZOJ 4612】 游戏
- 【TJOI & HEOI 2016】【BZOJ 4556】【JZOJ 4614】 游戏
- [BZOJ4556][Tjoi2016&Heoi2016]字符串(后缀数组+二分+st表+主席树)
- NKOJ 4000 (AHOI 2013)差异(后缀自动机/后缀数组+线段树/单调队列)
- NKOJ 2251 网络管理(树链剖分+树套树(树状数组+主席树))
- NKOJ 4090 找相同子串(后缀自动机/后缀数组+线段树)
- 后缀数组 (由倍增算法构造)
- 后缀数组(SA倍增算法)
- 后缀数组--学习笔记(倍增算法)
- Js如何调用本地应用程序
- Piontcut is not well-formed
- 史上最简单的SpringCloud教程 | 第六篇: 分布式配置中心(Spring Cloud Config)
- JSON.parse(),JSON.stringify(),jQuery.parseJSON()的用法
- flex-grow、flex-shrink、flex-basis详解
- NKOJ 4151 (TJOI 2016&HEOI 2016)字符串(后缀数组+倍增+主席树)
- 度盘会员破解
- Java JDK 环境变量的配置
- spring boot注解
- 使用cmd命令创建thinkphp5中的资源控制器
- 汉诺塔用递归算法
- win10下安装TensorFlow1.4.0-GPU
- 67 个拯救前端开发者的工具、库和资源
- POJ1284 Primitive Roots (欧拉函数 & 原根)