[达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
2017-04-05 21:40
501 查看
题意
给出一个长度为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 ,c ,d ,rank[N*2],sa ,height ,rmq [25],root ,sz,m,lg ; struct tree{int l,r,s;}t[N*20]; char s ; 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 ; 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; }
相关文章推荐
- BZOJ 4556: [Tjoi2016&Heoi2016]字符串 后缀数组 主席树
- [后缀数组 主席树] BZOJ 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556: [Tjoi2016&Heoi2016]字符串 (主席树+二分+后缀数组+ST表||后缀自动机+线段树合并+LCA)
- [BZOJ4556][Tjoi2016&Heoi2016]字符串(后缀数组+二分+st表+主席树)
- BZOJ4556 [Tjoi2016&Heoi2016]字符串 【后缀数组 + 主席树 + 二分 + ST表】
- bzoj4556: [Tjoi2016&Heoi2016]字符串 (后缀数组加主席树)
- 【BZOJ4556】[Tjoi2016&Heoi2016]字符串 后缀数组+二分+主席树+RMQ
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 主席树+二分+倍增+后缀自动机
- bzoj4556 [Tjoi2016&Heoi2016]字符串(SA+二分答案+线段树)
- 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分
- [BZOJ4552][Tjoi2016&Heoi2016]字符串-后缀数组-主席树
- [bzoj4556][Tjoi2016&Heoi2016]字符串
- [置顶] 4556: [Tjoi2016&Heoi2016]字符串(后缀自动机做法)
- 【BZOJ4556】字符串(后缀数组,主席树)
- bzoj 4556 [Tjoi2016&Heoi2016]字符串 二分+后缀数组+主席树+RMQ
- bzoj4556【TJOI2016&HEOI2016】字符串
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串
- Bzoj 4556: [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串