后缀数组总结
Posted loadingkkk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀数组总结相关的知识,希望对你有一定的参考价值。
fAKeT又来骗流量了(好像以前也没骗到
后缀数组好难啊。。。。。。
推荐博客:%%%DeepinC 这篇讲解除了太过详细以外都挺好
然后照例放题解包。
裸考后缀数组的题很少,大部分的题都是用后缀数组求出height数组,然后再结合各种各样的数据结构。
以下将height简称为h
相似子串
还是稍裸的。。。
给定两个本质不同子串的排名,求他们的公共前缀和公共后缀。
首先要根据排名求出子串。
因为要本质不同,所以对于sa[i],它与sa[i-1]的公共部分,即h[i],对子串个数不作贡献,只有长度大于h[i]的部分作出贡献
所以单点的贡献可以用后缀长度减去对应的h求得
然后对其求前缀和,查询时lower_bound即可得到对应字串的起点,用rank减去(起点-1)的前缀和,在加上起点的h即可得到串长
因为要前后分别统计,将串reverse一下在跑一遍sa即可。统计时可以用st表
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define N 200050 5 int n,m; 6 int bin[30],lg2[N]; 7 inline void init(int n){ 8 for(int i=0;i<=20;++i)bin[i]=1<<i; 9 for(int i=2;i<=n;++i)lg2[i]=lg2[i>>1]+1; 10 } 11 #define mmp make_pair 12 #define fir first 13 #define sec second 14 struct SA{ 15 char s[N]; 16 int sa[N],rk[N],h[N],st[N][18]; 17 LL h2[N]; 18 inline void getsa(int m){ 19 int t1[N],t2[N],t3[N],*x=t1,*y=t2,*c=t3; 20 memset(c,0,sizeof(int)*(m+1)); 21 for(int i=1;i<=n;++i)c[x[i]=s[i]-‘a‘+1]++; 22 for(int i=1;i<=m;++i)c[i]+=c[i-1]; 23 for(int i=1;i<=n;++i)sa[c[x[i]]--]=i; 24 if(m==n)++m; 25 for(int len=1;len<=n&&(m^n);len<<=1){ 26 int num=0; 27 for(int i=n-len+1;i<=n;++i)y[++num]=i; 28 for(int i=1;i<=n;++i)if(sa[i]>len)y[++num]=sa[i]-len; 29 memset(c,0,sizeof(int)*(m+1)); 30 for(int i=1;i<=n;++i)++c[x[i]]; 31 for(int i=1;i<=m;++i)c[i]+=c[i-1]; 32 for(int i=n;i;--i)sa[c[x[y[i]]]--]=y[i]; 33 swap(x,y); 34 memset(x,0,sizeof(int)*(n+1)); 35 x[sa[1]]=1;m=1; 36 for(int i=2;i<=n;++i) 37 x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+len]==y[sa[i-1]+len])?m:++m; 38 } 39 for(int i=1;i<=n;++i)rk[sa[i]]=i; 40 } 41 inline void geth(){ 42 for(int i=1,k=0;i<=n;h[rk[i++]]=k,k=k?k-1:0) 43 while(s[i+k]==s[sa[rk[i]-1]+k])++k; 44 for(int i=1;i<=n;++i)h2[i]=h2[i-1]+n-sa[i]+1-h[i]; 45 } 46 inline void getst(){ 47 for(int i=1;i<=n;++i)st[i][0]=h[i]; 48 for(int j=1;j<=lg2[n];++j) 49 for(int i=1;i<=n;++i) 50 st[i][j]=min(st[i][j-1],st[i+bin[j-1]][j-1]); 51 } 52 inline int ask(int l,int r){ 53 if(l>r)swap(l,r); 54 // cout<<l<<" "<<r<<endl; 55 if(l==r)return n-sa[l]+1; 56 return min(st[l+1][lg2[r-l]],st[r-bin[lg2[r-l]]+1][lg2[r-l]]); 57 } 58 inline pair<int,int> getpos(LL rank){ 59 int t=upper_bound(h2+1,h2+n+1,rank-1)-h2; 60 return mmp(sa[t],h[t]+rank-h2[t-1]); 61 } 62 }t1,t2; 63 int main(){ 64 // freopen("da.in","r",stdin); 65 //freopen("my.out","w",stdout); 66 scanf("%d%d",&n,&m); 67 scanf("%s",t1.s+1); 68 for(int i=1;i<=n;++i)t2.s[i]=t1.s[i]; 69 reverse(t2.s+1,t2.s+n+1); 70 init(n); 71 t1.getsa(27);t1.geth();t1.getst(); 72 t2.getsa(27);t2.geth();t2.getst(); 73 //cout<<t1.h2[n]<<endl; 74 //for(int i=1;i<=n;++i)cout<<t1.sa[i]<<" "<<t1.h[i]<<" "<<t1.h2[i]<<endl; 75 LL r1,r2; 76 //cout<<"rk1:"<<t1.rk[1]<<endl; 77 //for(int i=1;i<=t1.h2[n];++i){ 78 // pair<int,int>x=t1.getpos(i); 79 // printf("%d %d ",x.fir,x.sec); 80 //} 81 for(int i=1,p1,p2;i<=m;++i){ 82 pair<int,int>x,y; 83 scanf("%lld%lld",&r1,&r2); 84 if(r2>t1.h2[n]){puts("-1");continue;} 85 x=t1.getpos(r1);y=t1.getpos(r2); 86 // printf("x:%d %d ",x.fir,x.sec); 87 // printf("y:%d %d ",y.fir,y.sec); 88 int mi=min(x.sec,y.sec); 89 p1=t1.ask(t1.rk[x.fir],t1.rk[y.fir]); 90 if(mi<p1)p1=mi; 91 x.fir=n-(x.fir+x.sec-1)+1; 92 y.fir=n-(y.fir+y.sec-1)+1; 93 // printf("x2:%d %d ",x.fir,x.sec); 94 // printf("y2:%d %d ",y.fir,y.sec); 95 p2=t2.ask(t2.rk[x.fir],t2.rk[y.fir]); 96 //cout<<p2<<endl; 97 if(mi<p2)p2=mi; 98 // cout<<p1<<p2<<endl; 99 printf("%lld ",1ll*p1*p1+1ll*p2*p2); 100 } 101 }
品酒大会
NOI2015的题。sa+单调栈+st表
先跑sa,求出h数组
考虑用单调栈求出每个点的h作为区间最小值的区间
那么这个区间(最小值的位置为x)对数量的贡献为x左区间的长度*x右区间的长度。
对最值的贡献为x左区间的最大值*x右区间的最大值与x左区间的最小值*x右区间的最小值,二者取max
最后倒扫一遍传递贡献即可。记得开long long
如果你看不懂上面这一段就当我yasei
还是详细点吧。
首先我的数组下标是基于后缀的排名的,即下标为排名
同时下标也是h的下标。是不是好理解一点了?
然后就没有然后了,然后就上代码了。
1 #include<bits/stdc++.h> 2 #define N 600050 3 #define LL long long 4 namespace ae86{ 5 const int bufl=1<<15; 6 char buf[bufl],*s=buf,*t=buf; 7 inline int fetch(){ 8 if(s==t){t=(s=buf)+fread(buf,1,bufl,stdin);if(s==t)return EOF;} 9 return*s++; 10 } 11 inline int read(){ 12 int a=0,b=1,c=fetch(); 13 while(!isdigit(c))b^=c==‘-‘,c=fetch(); 14 while(isdigit(c))a=a*10+c-48,c=fetch(); 15 return b?a:-a; 16 } 17 } 18 using ae86::read; 19 using namespace std; 20 int n; 21 int a[N]; 22 char s[N]; 23 int sa[N],rk[N],h[N]; 24 inline void getsa(char *s,int m){ 25 static int t1[N],t2[N],t3[N],*x=t1,*y=t2,*c=t3; 26 memset(c,0,sizeof(int)*(m+1)); 27 for(int i=1;i<=n;++i)++c[x[i]=s[i]-‘a‘+1]; 28 for(int i=1;i<=m;++i)c[i]+=c[i-1]; 29 for(int i=1;i<=n;++i)sa[c[x[i]]--]=i; 30 for(int len=1;len<=n;len<<=1){ 31 int num=0; 32 for(int i=n-len+1;i<=n;++i)y[++num]=i; 33 for(int i=1;i<=n;++i)if(sa[i]>len)y[++num]=sa[i]-len; 34 memset(c,0,sizeof(int)*(m+1)); 35 for(int i=1;i<=n;++i)++c[x[i]]; 36 for(int i=1;i<=m;++i)c[i]+=c[i-1]; 37 for(int i=n;i;--i)sa[c[x[y[i]]]--]=y[i]; 38 swap(x,y);memset(x,0,sizeof(int)*(n+1)); 39 x[sa[1]]=m=1; 40 for(int i=2;i<=n;++i) 41 x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+len]==y[sa[i-1]+len])?m:++m; 42 if(m==n)break; 43 } 44 for(int i=1;i<=n;++i)rk[sa[i]]=i; 45 for(int i=1,k=0;i<=n;h[rk[i++]]=k,k=k?k-1:0) 46 while(s[i+k]==s[sa[rk[i]-1]+k])++k; 47 } 48 int st1[20][N],st2[20][N],lg2[N],bin[30]; 49 int dq[N],ba,l[N],r[N]; 50 inline void init(){ 51 for(int i=0;i<=25;++i)bin[i]=1<<i; 52 for(int i=2;i<=n;++i)lg2[i]=lg2[i>>1]+1; 53 for(int i=1;i<=n;++i)st1[0][i]=st2[0][i]=a[sa[i]]; 54 for(int j=1;j<=lg2[n];++j) 55 for(int i=1;i<=n;++i) 56 st1[j][i]=min(st1[j-1][i],st1[j-1][i+bin[j-1]]), 57 st2[j][i]=max(st2[j-1][i],st2[j-1][i+bin[j-1]]); 58 h[1]=-100;h[n+1]=-50;ba=1;dq[1]=1; 59 for(int i=2;i<=n+1;++i){ 60 l[i]=r[i]=i; 61 while(h[i]<=h[dq[ba]])r[dq[ba]]=i-1,--ba; 62 l[i]=dq[ba]+1;dq[++ba]=i; 63 } 64 } 65 inline int ask1(int l,int r){ 66 return min(st1[lg2[r-l+1]][l],st1[lg2[r-l+1]][r-bin[lg2[r-l+1]]+1]); 67 } 68 inline int ask2(int l,int r){ 69 return max(st2[lg2[r-l+1]][l],st2[lg2[r-l+1]][r-bin[lg2[r-l+1]]+1]); 70 } 71 LL an1[N],an2[N]; 72 int main(){ 73 scanf("%d%s",&n,s+1); 74 for(int i=1;i<=n;++i)a[i]=read(); 75 getsa(s,27); 76 //for(int i=1;i<=n;++i)printf("sa[%d]=%d %d ",i,sa[i],h[i]); 77 init(); 78 memset(an2,-0x3f,sizeof(LL)*(n+2)); 79 for(int i=2;i<=n;++i){ 80 // cout<<l[i]<<" "<<r[i]<<endl; 81 an1[h[i]]+=1ll*(i-l[i]+1)*(r[i]-i+1); 82 LL t=1ll*ask2(l[i]-1,i-1)*ask2(i,r[i]); 83 an2[h[i]]=max(t,an2[h[i]]); 84 t=1ll*ask1(l[i]-1,i-1)*ask1(i,r[i]); 85 an2[h[i]]=max(t,an2[h[i]]); 86 } 87 for(int i=n-2;~i;--i){ 88 an1[i]+=an1[i+1]; 89 if(an1[i+1])an2[i]=max(an2[i+1],an2[i]); 90 } 91 for(int i=0;i<n;++i)printf("%lld %lld ",an1[i],an2[i]==an2[n+1]?0:an2[i]); 92 return 0; 93 }
字符串
HEOI的题,sa+主席树+二分答案
求区间内子串和一个固定串的lcp
照例先跑sa,处理出h。照例有st表处理h。(哪来的例?
然后我们的操作的下标基于h的下标,特殊的有标注
考虑主席树(基于原串),主席树的用途为查询前趋和后继。
具体操作为先查询rank,然后查询rank的串和rank+1的串。
然后发现因为有区间的限制每个子串能够作出贡献的最大长度不同。
考虑二分答案,二分出最后的长度,
用主席树查询(固定串后缀排名的)前趋和后继,
再用st表check最大长度是否符合即可
次题数据弱,暴力从固定串排名向左右分别扩展可过。(跑得比正解快。。。
1 #include<bits/stdc++.h> 2 #define N 200050 3 using namespace std; 4 int n,m; 5 char s[N]; 6 struct SA{ 7 int sa[N],h[N],rk[N]; 8 int rt[N],lc[N*20],rc[N*20],sum[N*20],tot; 9 int st[N][18],lg2[N],bin[30]; 10 inline void getsa(char *s,int m){ 11 int t1[N],t2[N],t3[N],*x=t1,*y=t2,*c=t3; 12 memset(c,0,sizeof(int)*(m+1)); 13 for(int i=1;i<=n;++i)++c[x[i]=s[i]-‘a‘+1]; 14 for(int i=1;i<=m;++i)c[i]+=c[i-1]; 15 for(int i=1;i<=n;++i)sa[c[x[i]]--]=i; 16 for(int len=1;len<=n;len<<=1){ 17 int num=0; 18 for(int i=n-len+1;i<=n;++i)y[++num]=i; 19 for(int i=1;i<=n;++i)if(sa[i]>len)y[++num]=sa[i]-len; 20 memset(c,0,sizeof(int)*m); 21 for(int i=1;i<=n;++i)++c[x[i]]; 22 for(int i=1;i<=m;++i)c[i]+=c[i-1]; 23 for(int i=n;i;--i)sa[c[x[y[i]]]--]=y[i]; 24 swap(x,y);memset(x,0,sizeof(int)*(n+1)); 25 x[sa[1]]=m=1; 26 for(int i=2;i<=n;++i) 27 x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+len]==y[sa[i-1]+len])?m:++m; 28 if(m==n)break; 29 } 30 for(int i=1;i<=n;++i)rk[sa[i]]=i; 31 } 32 inline void geth(char *s){ 33 for(int i=1,k=0;i<=n;h[rk[i++]]=k,k=(!k)?0:k-1) 34 while(s[i+k]==s[sa[rk[i]-1]+k])++k; 35 } 36 inline void add(int &g,int f,int l,int r,int x){ 37 if(!g)g=++tot;sum[g]=sum[f]+1; 38 if(l==r)return; 39 const int m=l+r>>1; 40 if(x<=m)rc[g]=rc[f],add(lc[g],lc[f],l,m,x); 41 else lc[g]=lc[f],add(rc[g],rc[f],m+1,r,x); 42 } 43 inline int getrank(int g,int f,int l,int r,int x){ 44 if(l==r)return sum[g]-sum[f]; 45 if(!(sum[g]-sum[f]))return 0; 46 const int m=l+r>>1; 47 if(x<=m)return getrank(lc[g],lc[f],l,m,x); 48 return sum[lc[g]]-sum[lc[f]]+getrank(rc[g],rc[f],m+1,r,x); 49 } 50 inline int getnum(int g,int f,int l,int r,int x){ 51 if(l==r)return l; 52 const int m=l+r>>1; 53 if(x<=sum[lc[g]]-sum[lc[f]]) 54 return getnum(lc[g],lc[f],l,m,x); 55 return getnum(rc[g],rc[f],m+1,r,x-(sum[lc[g]]-sum[lc[f]])); 56 } 57 inline void init(){ 58 for(int i=0;i<=20;++i)bin[i]=1<<i; 59 for(int i=1;i<=n;++i) 60 add(rt[i],rt[i-1],1,n,rk[i]); 61 for(int i=2;i<=n;++i)lg2[i]=lg2[i>>1]+1; 62 for(int i=1;i<=n;++i)st[i][0]=h[i]; 63 for(int j=1;j<=lg2[n];++j) 64 for(int i=1;i<=n;++i) 65 st[i][j]=min(st[i][j-1],st[i+bin[j-1]][j-1]); 66 } 67 inline int askmin(int x,int y){ 68 x=rk[x];y=rk[y]; 69 if(x==y)return n-sa[x]+1; 70 if(x>y)swap(x,y); 71 return min(st[x+1][lg2[y-x]],st[y-bin[lg2[y-x]]+1][lg2[y-x]]); 72 } 73 inline bool check(int l,int r,int c,int len){ 74 int t=getrank(rt[r],rt[l-1],1,n,rk[c]); 75 if(t){ 76 int k=getnum(rt[r],rt[l-1],1,n,t),p=askmin(sa[k],c); 77 if(p>=len)return true; 78 } 79 if(t!=r-l+1){ 80 int k=getnum(rt[r],rt[l-1],1,n,t+1),p=askmin(sa[k],c); 81 if(p>=len)return true; 82 } 83 return false; 84 } 85 }K; 86 int main(){ 87 scanf("%d%d%s",&n,&m,s+1); 88 K.getsa(s,27);K.geth(s);K.init(); 89 for(int i=1,l,r,mid,a,b,c,d;i<=m;++i){ 90 scanf("%d%d%d%d",&a,&b,&c,&d); 91 l=0,r=min(b-a+1,d-c+1)+1; 92 while(l+1<r){ 93 mid=l+r>>1; 94 if(K.check(a,b-mid+1,c,mid))l=mid; 95 else r=mid; 96 } 97 printf("%d ",l); 98 } 99 return 0; 100 }
喵星球上的点名
咕咕咕
以上是关于后缀数组总结的主要内容,如果未能解决你的问题,请参考以下文章