BZOJ4556:[TJOI\HEOI2016]字符串(后缀数组,主席树,二分,ST表)
Description
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?
Input
输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n
Output
对于每一次询问,输出答案。
Sample Input
5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4
Sample Output
1
1
2
2
2
1
2
2
2
Solution
设$suf_i$表示后缀$i$,
首先考虑二分一个答案$mid$,那么现在问题转化成求$suf_a$到$suf_{b-mid+1}$的这些个后缀中,是否有一个后缀和$suf(c)$的$lcp$大于等于$mid$。
设$SA_i$表示后缀排序排名为$i$的后缀,$Rank_i$表示$suf_i$的后缀排序排名。
由于对于$suf_c$,我们可以用二分+$ST$表求出后缀排序中的可行区间,可行区间内的后缀和$suc_c$的$lcp$长度都大于等于$mid$。现在问题转化为了求$suf_a$到$suf_{b-mid+1}$的这些后缀是否哪个后缀的$Rank$值在可行区间内,这个用一个主席树就好了。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define N (100009) 5 using namespace std; 6 7 struct Sgt{int ls,rs,val;}Segt[N*18]; 8 int Root[N],sgt_num; 9 int n,q,m=26,a,b,c,d; 10 int ST[N][18],LOG2[N]; 11 int wa[N],wb[N],wt[N],SA[N],Height[N],Rank[N]; 12 char r[N]; 13 14 inline int read() 15 { 16 int x=0,w=1; char c=getchar(); 17 while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();} 18 while (c>='0' && c<='9') x=x*10+c-'0', c=getchar(); 19 return x*w; 20 } 21 22 bool cmp(int *y,int a,int b,int k) 23 { 24 int arank1=y[a]; 25 int brank1=y[b]; 26 int arank2=a+k>=n?-1:y[a+k]; 27 int brank2=b+k>=n?-1:y[b+k]; 28 return arank1==brank1 && arank2==brank2; 29 } 30 31 void Build_SA() 32 { 33 int *x=wa,*y=wb; 34 for (int i=0; i<m; ++i) wt[i]=0; 35 for (int i=0; i<n; ++i) ++wt[x[i]=r[i]-'a']; 36 for (int i=1; i<m; ++i) wt[i]+=wt[i-1]; 37 for (int i=n-1; i>=0; --i) SA[--wt[x[i]]]=i; 38 39 for (int j=1; j<=n; j<<=1) 40 { 41 int p=0; 42 for (int i=n-j; i<n; ++i) y[p++]=i; 43 for (int i=0; i<n; ++i) if (SA[i]>=j) y[p++]=SA[i]-j; 44 45 for (int i=0; i<m; ++i) wt[i]=0; 46 for (int i=0; i<n; ++i) ++wt[x[y[i]]]; 47 for (int i=1; i<m; ++i) wt[i]+=wt[i-1]; 48 for (int i=n-1; i>=0; --i) SA[--wt[x[y[i]]]]=y[i]; 49 50 m=1; swap(x,y); 51 x[SA[0]]=0; 52 for (int i=1; i<n; ++i) 53 x[SA[i]]=cmp(y,SA[i],SA[i-1],j)?m-1:m++; 54 if (m>=n) break; 55 } 56 } 57 58 void Build_Height() 59 { 60 for (int i=0; i<n; ++i) Rank[SA[i]]=i; 61 int k=0; 62 for (int i=0; i<n; ++i) 63 { 64 if (!Rank[i]) continue; 65 if (k) k--; 66 int j=SA[Rank[i]-1]; 67 while (r[i+k]==r[j+k]) ++k; 68 Height[Rank[i]]=k; 69 } 70 } 71 72 void Build_ST() 73 { 74 for (int i=2; i<=n; ++i) LOG2[i]=LOG2[i>>1]+1; 75 for (int i=0; i<n; ++i) ST[i][0]=Height[i]; 76 for (int j=1; j<=17; ++j) 77 for (int i=0; i+(1<<j)-1<n; ++i) 78 ST[i][j]=min(ST[i][j-1],ST[i+(1<<j-1)][j-1]); 79 } 80 81 int Query(int l,int r) 82 { 83 int k=LOG2[r-l+1]; 84 return min(ST[l][k],ST[r-(1<<k)+1][k]); 85 } 86 87 int Update(int pre,int l,int r,int x) 88 { 89 int now=++sgt_num; 90 Segt[now]=Segt[pre]; 91 Segt[now].val++; 92 if (l==r) return now; 93 int mid=(l+r)>>1; 94 if (x<=mid) Segt[now].ls=Update(Segt[now].ls,l,mid,x); 95 else Segt[now].rs=Update(Segt[now].rs,mid+1,r,x); 96 return now; 97 } 98 99 int Query(int u,int v,int l,int r,int l1,int r1) 100 { 101 if (l>r1 || r<l1) return 0; 102 if (l1<=l && r<=r1) return Segt[v].val-Segt[u].val; 103 int mid=(l+r)>>1; 104 return Query(Segt[u].ls,Segt[v].ls,l,mid,l1,r1)+Query(Segt[u].rs,Segt[v].rs,mid+1,r,l1,r1); 105 } 106 107 bool check(int lim) 108 { 109 int l,r,L,R; 110 l=0,r=Rank[c]-1,L=Rank[c]; 111 while (l<=r) 112 { 113 int mid=(l+r)>>1; 114 if (Query(mid+1,Rank[c])>=lim) L=mid,r=mid-1; 115 else l=mid+1; 116 } 117 l=Rank[c]+1,r=n-1,R=Rank[c]; 118 while (l<=r) 119 { 120 int mid=(l+r)>>1; 121 if (Query(Rank[c]+1,mid)>=lim) R=mid,l=mid+1; 122 else r=mid-1; 123 } 124 if (Query(Root[a],Root[b-lim+2],0,n,L,R)) return true; 125 return false; 126 } 127 128 int main() 129 { 130 n=read(); q=read(); 131 scanf("%s",r); 132 Build_SA(); Build_Height(); 133 Build_ST(); 134 for (int i=1; i<=n; ++i) 135 Root[i]=Update(Root[i-1],0,n,Rank[i-1]); 136 while (q--) 137 { 138 a=read()-1; b=read()-1; c=read()-1; d=read()-1; 139 int l=1,r=min(b-a+1,d-c+1),ans=0; 140 while (l<=r) 141 { 142 int mid=(l+r)>>1; 143 if (check(mid)) ans=mid, l=mid+1; 144 else r=mid-1; 145 } 146 printf("%d\n",ans); 147 } 148 }