后缀数组总结

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,处理出h数组,

然后直接对每个询问在h数组上暴力向两边扩展统计答案即可AC

但我们还是要找一个合法的复杂度。

利用st表+二分可以在log的时间复杂度内找到对应的区间。

用主席树维护一下即可(好像叫数颜色???)然后第一问就ok了

对于第二问,可以用莫队,把上一问处理出的区间离线,

在每只喵第一次加入时加入m-i,这只喵完全滚粗时再减去m-i。

还可以用树状数组,详见:%%%

View Code

upd:后缀数组专题二

外星联络

裸的后缀数组,考察对h的应用,如果对h数组真正理解就是秒切

SvT

(我并不知道题目名字是什么鬼)

还是稍简单的一道题。

考虑将单次询问按rank排序,存入数组,之后分治解决。

这里粘一下函数,a数组中存的是排名,ask返回h数组区间最小值的位置。

1 int solve(int l,int r){
2     if(l==r)return 0;
3     int t=ask(a[l]+1,a[r]);
4     int k=lower_bound(a+l,a+r+1,t)-a;
5     int ret=h[t]*(r-k+1)*(k-l);
6     ret+=solve(l,k-1);
7     ret+=solve(k,r);
8     return ret;
9 }

其实看代码已经很显然了。

好多人看到这种打法都会疑问复杂度为nlog2n(你一眼看穿我也没话说

然后这里证明一下复杂度为nlogn

考虑lower_bound调用的次数,每次调用都会产生一个左区间的右端点,

所以lower_bound至多调用n次,复杂度为nlogn

股市的预测

新思想:关键点

考虑枚举相同串的长度,设为len,

每隔len设立一个关键点。

在关键点处统计包含这个关键点的长度为len的串对答案的贡献

具体做法:对关键点i,找出对应的j=i+len+m,求出向左和向右最长的匹配区间的长度,记为l和r,对len取min。

统计答案用max(0,l+r-len);

跳蚤

(然而并不是uoj的题)

想题的心路历程:

一开始发现字典序最大的后缀对答案的影响很大。然后自以为答案处在最后一个串上。然后伪了。

扩展一下,当最后的串被砍断后,答案出在倒数第二个串上,然后想二分(答案出现串的排名)

然后好像没伪。但二分完了之后,知道了答案串位置的排名,却不知道具体长度。

然后考虑二分(答案的长度),在对应长度处固定砍一刀。然后经rnb点拨,这玩意不具有单调性。

又经rnb点拨,其实应该二分(答案的长度的上界),区别就是必须在这个二分出的长度以内砍一刀,

而不是在长度处砍一刀。然后这个东西是具有单调性的。

 1 #include<bits/stdc++.h>
 2 #define N 100050
 3 using namespace std;
 4 int n,k,tot;
 5 char s[N];
 6 int sa[N],rk[N],h[N];
 7 inline void getsa(char *s,int m){
 8     static int t[3][N],*x=t[0],*y=t[1],*c=t[2];
 9     memset(c,0,sizeof(int)*(m+1));
10     for(int i=1;i<=n;++i)++c[x[i]=s[i]-'a'+1];
11     for(int i=1;i<=m;++i)c[i]+=c[i-1];
12     for(int i=1;i<=n;++i)sa[c[x[i]]--]=i;
13     for(int len=1;len<=n;len<<=1){
14         int num=0;
15         for(int i=n-len+1;i<=n;++i)y[++num]=i;
16         for(int i=1;i<=n;++i)if(sa[i]>len)y[++num]=sa[i]-len;
17         memset(c,0,sizeof(int)*(m+1));
18         for(int i=1;i<=n;++i)++c[x[i]];
19         for(int i=1;i<=m;++i)c[i]+=c[i-1];
20         for(int i=n;i;--i)sa[c[x[y[i]]]--]=y[i];
21         swap(x,y);memset(x,0,sizeof(int)*(n+1));
22         x[sa[1]]=m=1;
23         for(int i=2;i<=n;++i)
24             x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+len]==y[sa[i-1]+len])?m:++m;
25         if(m==n)break;
26     }
27     for(int i=1;i<=n;++i)rk[sa[i]]=i;
28     for(int i=1,k=0;i<=n;h[rk[i++]]=k,k=k?k-1:0)
29         while(s[i+k]==s[sa[rk[i]-1]+k])++k;
30 }
31 struct node{
32     int l,r;
33     friend bool operator < (const node &a,const node &b){
34         return a.l<b.l;
35     }
36 }q[N];
37 inline bool check(){
38     sort(q+1,q+tot+1);
39     static int ma[N];memset(ma,0,sizeof(ma));
40     for(int i=1;i<=tot;++i)ma[q[i].r]=max(ma[q[i].r],i);
41     int ju=0;
42     for(int i=1,las=0,ts=1;i<=n;++i){
43         while(ts<=tot&&q[ts].l==i)++ts;
44         if(ma[i]>las)las=ts-1,++ju;
45     }
46     return ju<=k;
47 }
48 int main(){
49     scanf("%d%s",&k,s+1);n=strlen(s+1);--k;
50     getsa(s,27);
51     int l,r,pos,mid;
52     for(l=n-1;;--l)if(s[sa[l]]!=s[sa[l+1]])break;
53     r=n;
54     while(l+1<r){
55         mid=l+r>>1;
56         tot=0;
57         for(int i=mid+1,mi=n+1;i<=n;++i){
58             mi=min(mi,h[i]);
59             q[++tot]=(node){sa[i],sa[i]+mi-1};
60         }
61         if(check())r=mid;
62         else l=mid;
63     }
64     pos=r;
65     r=n-sa[pos]+1;l=0;
66     while(l+1<r){
67         mid=l+r>>1;
68         tot=0;q[++tot]=(node){sa[pos],sa[pos]+mid-1};
69         for(int i=pos+1,mi=mid;i<=n;++i){
70             mi=min(mi,h[i]);
71             q[++tot]=(node){sa[i],sa[i]+mi-1};
72         }
73         if(check())r=mid;
74         else l=mid;
75     }
76     for(int i=sa[pos];i<=sa[pos]+r-1;++i)putchar(s[i]);
77     puts("");
78     return 0;
79 }
View Code
原文地址:https://www.cnblogs.com/loadingkkk/p/12088518.html