[达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
来源:互联网 发布:监控软件流量 编辑:程序博客网 时间:2024/06/02 06:57
题意
给出一个长度为n的字符串,有m个询问a b c d表示求s[a..b]的子串和s[c..d]的最长公共前缀的最大值。
n,m<=100000
分析
先求一波后缀数组,然后考虑二分答案,将其转变成判定性问题。
设当前长度为len,那么我们可以在height数组内找到一个区间[L,R],使得该区间内的任何下标与s[c..d]的最长公共前缀不小于len。
然后我们可以对rank数组建一棵可持久化线段树,再查询rank[a,b-len+1]内有没有数在区间[L,R]内即可。
复杂度O(nlog2)
代码
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int N=100005;int n,b[N],c[N],d[N],rank[N*2],sa[N],height[N],rmq[N][25],root[N],sz,m,lg[N];struct tree{int l,r,s;}t[N*20];char s[N];void get_sa(int n){ for (int i=1;i<=n;i++) b[s[i]]++; for (int i=1;i<=n;i++) b[i]+=b[i-1]; for (int i=n;i>=1;i--) c[b[s[i]]--]=i; int t=0; for (int i=1;i<=n;i++) { if (s[c[i]]!=s[c[i-1]]) t++; rank[c[i]]=t; } int j=1; while (j<=n) { for (int i=1;i<=n;i++) b[i]=0; for (int i=1;i<=n;i++) b[rank[i+j]]++; for (int i=1;i<=n;i++) b[i]+=b[i-1]; for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i; for (int i=1;i<=n;i++) b[i]=0; for (int i=1;i<=n;i++) b[rank[i]]++; for (int i=1;i<=n;i++) b[i]+=b[i-1]; for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i]; t=0; for (int i=1;i<=n;i++) { if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++; c[d[i]]=t; } for (int i=1;i<=n;i++) rank[i]=c[i]; if (t==n) break; j*=2; } for (int i=1;i<=n;i++) sa[rank[i]]=i;}void get_height(int n){ int k=0; for (int i=1;i<=n;i++) { if (k) k--; int j=sa[rank[i]-1]; while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++; height[rank[i]]=k; }}void get_rmq(){ int len=lg[n]; for (int i=1;i<=n;i++) rmq[i][0]=height[i]; for (int j=1;j<=len;j++) for (int i=1;i<=n-(1<<j)+1;i++) rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);}int get_min(int l,int r){ int len=r-l+1; return min(rmq[l][lg[len]],rmq[r-(1<<lg[len])+1][lg[len]]);}void ins(int &d,int p,int l,int r,int x){ d=++sz; t[d]=t[p];t[d].s++; if (l==r) return; int mid=(l+r)/2; if (x<=mid) ins(t[d].l,t[p].l,l,mid,x); else ins(t[d].r,t[p].r,mid+1,r,x);}int query(int d,int p,int l,int r,int x,int y){ if (x>y) return 0; if (l==x&&r==y) return t[d].s-t[p].s; int mid=(l+r)/2; return query(t[d].l,t[p].l,l,mid,x,min(y,mid))+query(t[d].r,t[p].r,mid+1,r,max(mid+1,x),y);}bool check(int len,int a,int b,int c,int d){ int l=rank[c]+1,r=n; while (l<=r) { int mid=(l+r)/2; if (get_min(rank[c]+1,mid)>=len) l=mid+1; else r=mid-1; } int R=l-1; l=1,r=rank[c]; while (l<=r) { int mid=(l+r)/2; if (get_min(mid+1,rank[c])>=len) r=mid-1; else l=mid+1; } int L=r+1; if (L>R) return 0; if (query(root[b-len+1],root[a-1],1,n,L,R)) return 1; else return 0;}int main(){ scanf("%d%d",&n,&m); scanf("%s",s+1); get_sa(n); get_height(n); for (int i=1;i<=n;i++) lg[i]=log(i)/log(2); get_rmq(); for (int i=1;i<=n;i++) ins(root[i],root[i-1],1,n,rank[i]); for (int i=1;i<=m;i++) { int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d); int l=1,r=min(b-a+1,d-c+1); while (l<=r) { int mid=(l+r)/2; if (check(mid,a,b,c,d)) l=mid+1; else r=mid-1; } printf("%d\n",l-1); } return 0;}
0 0
- [达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
- [后缀数组 主席树] BZOJ 4556 [Tjoi2016&Heoi2016]字符串
- BZOJ 4556: [Tjoi2016&Heoi2016]字符串 后缀数组 主席树
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串 二分+后缀数组+主席树+RMQ
- 4556: [Tjoi2016&Heoi2016]字符串
- 4556: [Tjoi2016&Heoi2016]字符串
- 4556: [Tjoi2016&Heoi2016]字符串
- 4556: [Tjoi2016&Heoi2016]字符串 后缀自动机 详细
- bzoj-4551 [Tjoi2016&Heoi2016]树
- BZOJ 4551: [Tjoi2016&Heoi2016]树
- BZOJ 4551 [Tjoi2016&Heoi2016]树
- BZOJ 4551: [Tjoi2016&Heoi2016]树
- BZOJ4556: [Tjoi2016&Heoi2016]字符串
- Bzoj4556: [Tjoi2016&Heoi2016]字符串
- cmake 在qtcreator 编译中遇到的问题
- 远程控制路由器
- Android图片压缩加密上传
- jQuery轮播
- 欧拉回路输出路径 POJ
- [达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
- 所谓情商高,就是会说话
- 《递归递推练习》M
- BP算法的梯度激增,梯度消失
- Codeforces Round #376 (Div. 2) C. Socks
- 填补空缺值(平均值)
- Win32 SDK 学习笔记6
- CPU、MCU、MPU、DSP的区别和介绍
- 两字母交叉最少问题