后缀数组总结

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 }
View Code

字符串

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 }
View Code

喵星球上的点名

咕咕咕

以上是关于后缀数组总结的主要内容,如果未能解决你的问题,请参考以下文章

后缀数组(SA)总结

●后缀数组○十三个例题

后缀数组解题总结

后缀数组总结

后缀数组模板题总结

学习总结-后缀数组