后缀数组(SA)做题记录

本文属于抽象文学,读者大概是读不懂的(本来也是给自己写的

SA 真的是个好东西,好呀好东西。

基础定义

$sa$ 数组:后缀排序后排名为 $i$ 的后缀的起始位置下标。

$rk$ 数组:起始下标为 $i$ 的后缀的排名。

$height$ 数组:后缀排序后排名为 $i$ 和 $i-1$ 的最长公共前缀长度(Lcp)

模板:(小bug:在模板的第21行会出现越界的情况(访问到 $Y_{n+1},只有在 $X_i$ 出现 $0$ 的时候会出问题(加一即可规避))

             标准写法是写个 cmp 函数专门判断。

 1 char ch[N];
 2 struct Suffix_Array
 3 {
 4     ll m=200,X[N],Y[N],c[N],num[N],sa[N],height[N],rk[N],lg[N],st[N][23];
 5     void Sa()
 6     {
 7         for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
 8         for(int i=2;i<=m;i++)c[i]+=c[i-1];
 9         for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
10         for(int k=1,p=0;k<=n;k=k<<1,p=0)
11         {
12             for(int i=n-k+1;i<=n;i++)Y[++p]=i;
13             for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
14             for(int i=1;i<=m;i++)c[i]=0;
15             for(int i=1;i<=n;i++)c[X[i]]++;
16             for(int i=2;i<=m;i++)c[i]+=c[i-1];
17             for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
18             swap(X,Y);p=1;X[sa[1]]=1;
19             for(int i=2;i<=n;i++)
20             {
21                 if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
22                 else X[sa[i]]=++p;
23             }
24             if(p==n)return;m=p;
25         }
26     }
27     void Height()
28     {
29         ll k=0;
30         for(int i=1;i<=n;i++)rk[sa[i]]=i;
31         for(int i=1;i<=n;i++)
32         {
33             if(rk[i]==1)continue;
34             if(k)k--;
35             int j=sa[rk[i]-1];
36             while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
37             height[rk[i]]=k;
38         }
39     }
40     void St()
41     {
42         for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
43         for(int i=1;i<=n;i++)st[i][0]=height[i];
44         for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
45             st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
46     }
47     int Lcp(int l,int r) //原数组中 l,r的lcp长度 
48     {
49         if((l=rk[l])>(r=rk[r]))swap(l,r); //若是直接在后缀数组sa[i]上求就删除 
50         int d=lg[r-l++];
51         return min(st[l][d],st[r-(1<<d)+1][d]); 
52     }
53 }SA;
View Code

 题目:

1.P3809 【模板】后缀排序

板子

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+3;
 6 int n,m=200,X[N],Y[N],c[N],sa[N],height[N],rk[N];
 7 char ch[N];
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(p==n)return;m=p;
28     }
29 }
30 void Height()
31 {
32     int k=0;
33     for(int i=1;i<=n;i++)rk[sa[i]]=i;
34     for(int i=1;i<=n;i++)
35     {
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
40         height[rk[i]]=k;
41     }
42 }
43 int main()
44 {
45     cin>>(ch+1);n=strlen(ch+1);
46     Sa();Height();
47     for(int i=1;i<=n;i++)cout<<sa[i]<<" ";
48     return 0; 
49 }
View Code

 2.P2408 不同子串个数

利用 $height$ 数组的性质,总个数减去相邻两个的 Lcp 即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+3;
 6 ll n,m=200,X[N],Y[N],c[N],sa[N],height[N],rk[N];
 7 char ch[N];
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(p==n)return;m=p;
28     }
29 }
30 void Height()
31 {
32     int k=0;
33     for(int i=1;i<=n;i++)rk[sa[i]]=i;
34     for(int i=1;i<=n;i++)
35     {
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
40         height[rk[i]]=k;
41     }
42 }
43 int main()
44 {
45     cin>>n>>(ch+1);
46     Sa();Height();ll s=n*(n-1)/2+n;
47     for(int i=1;i<=n;i++)s-=height[i];
48     cout<<s<<endl;
49     return 0; 
50 }
View Code

3.P5546 [POI2000] 公共串

将所有串串用分隔符串在一起(一定要不同且最后一定不能有多的),分别染色。

然后就双指针暴力扫使得每种颜色都出现过然后求 Lcp 即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+13;
 6 int n,B,ok,col[N],cnt[N],lg[N],st[N][23];
 7 int m=2000,X[N],Y[N],c[N],sa[N],height[N],rk[N];
 8 char ch[N],ca[N];
 9 void Sa()
10 {
11     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
12     for(int i=2;i<=m;i++)c[i]+=c[i-1];
13     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
14     for(int k=1,p=0;k<=n;k=k<<1,p=0)
15     {
16         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
17         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
18         for(int i=1;i<=m;i++)c[i]=0;
19         for(int i=1;i<=n;i++)c[X[i]]++;
20         for(int i=2;i<=m;i++)c[i]+=c[i-1];
21         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
22         swap(X,Y);p=1;X[sa[1]]=1;
23         for(int i=2;i<=n;i++)
24         {
25             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
26             else X[sa[i]]=++p;
27         }
28         if(n==p)return;m=p;
29     }
30 }
31 void Height()
32 {
33     int k=0;
34     for(int i=1;i<=n;i++)rk[sa[i]]=i;
35     for(int i=1;i<=n;i++)
36     {
37         if(rk[i]==1)continue;
38         if(k)k--;
39         int j=sa[rk[i]-1];
40         while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
41         height[rk[i]]=k;
42     }
43 }
44 void Add(int x)
45 {
46     int y=col[x];
47     if(!y)return;
48     if(!cnt[y])ok++;
49     cnt[y]++;
50 }
51 void Del(int x)
52 {
53     int y=col[x];
54     if(!y)return;
55     cnt[y]--;
56     if(!cnt[y])ok--; 
57 }
58 void St()
59 {
60     for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
61     for(int i=1;i<=n;i++)st[i][0]=height[i];
62     for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
63         st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
64 }
65 int Ask(int l,int r)
66 {
67     int d=lg[r-l+1];
68     return min(st[l][d],st[r-(1<<d)+1][d]); 
69 }
70 int main()
71 {
72     cin>>B;
73     for(int t=1;t<=B;t++)
74     {
75         cin>>(ca+1);int z=strlen(ca+1);
76         for(int i=1;i<=z;i++)ch[++n]=ca[i],col[n]=t; 
77         ch[++n]='{';
78     }
79     n--;Sa();Height();St();int i=0;
80     while(ok<B&&i<n)i++,Add(sa[i]);
81     if(ok<B){cout<<0;return 0;}
82     int ans=Ask(2,i);
83     for(int j=1;i<=n&&j<=n;)
84     {
85         Del(sa[j]),j++;
86         while(ok<B&&i<n)i++,Add(sa[i]);
87         if(ok<B){cout<<ans;return 0;}
88         ans=max(ans,Ask(j+1,i));
89     }
90     cout<<ans;
91 }
View Code

4.P4248 [AHOI2013] 差异

分别考虑每个 $height_i$ 的贡献,相当于就是求全局的所有子区间的 $min$ 之和。

这就是经典题,具体的可以看 这里

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+3;
 6 ll n,m=200,X[N],Y[N],c[N],sa[N],height[N],rk[N],sta[N],ans[N];
 7 char ch[N];
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(p==n)return;m=p;
28     }
29 }
30 void Height()
31 {
32     int k=0;
33     for(int i=1;i<=n;i++)rk[sa[i]]=i;
34     for(int i=1;i<=n;i++)
35     {
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
40         height[rk[i]]=k;
41     }
42 }
43 ll Work()
44 {
45     ll top=0,s=0;
46     for(int i=1;i<=n;i++)
47     {
48         while(top&&height[sta[top]]>=height[i])top--;
49         ans[i]=ans[sta[top]]+height[i]*(i-sta[top]);
50         s+=ans[i];sta[++top]=i;
51     }
52     return 2*s;
53 }
54 int main()
55 {
56     cin>>(ch+1);n=strlen(ch+1);
57     Sa();Height();ll s=n*(n-1)*(n+1)/2;
58     cout<<s-Work(); 
59 }
View Code

5.P3181 [HAOI2016] 找相同字符

和 4. 本质相同。 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+3;
 6 ll n,na,nb,m,c[N],X[N],Y[N],sa[N],height[N],rk[N],sta[N],ans[N];
 7 char ch[N],ca[N],cb[N];
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(p==n)return;m=p;
28     }
29 }
30 void Height()
31 {
32     int k=0;
33     for(int i=1;i<=n;i++)rk[sa[i]]=i;
34     for(int i=1;i<=n;i++)
35     {
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
40         height[rk[i]]=k; 
41     }
42 }
43 ll Work()
44 {
45     ll top=0,s=0;
46     for(int i=1;i<=n;i++)
47     {
48         while(top&&height[sta[top]]>=height[i])top--;
49         ans[i]=ans[sta[top]]+height[i]*(i-sta[top]);
50         s+=ans[i];sta[++top]=i;
51     }
52     return s;
53 }
54 ll Solve()
55 {
56     m=200;
57     for(int i=1;i<N;i++)sa[i]=height[i]=rk[i]=X[i]=Y[i]=c[i]=0;
58     Sa();Height();
59     return Work();
60 }
61 int main()
62 {
63     cin>>(ca+1)>>(cb+1);na=strlen(ca+1);nb=strlen(cb+1);
64     ll sa=0,sb=0,s=0;
65     for(int i=1;i<=na;i++)ch[i]=ca[i];
66     n=na;sa=Solve();
67     for(int i=1;i<=nb;i++)ch[i]=cb[i];
68     n=nb;sb=Solve();ch[++n]='#';
69     for(int i=1;i<=na;i++)ch[++n]=ca[i];
70     s=Solve();
71     cout<<s-sa-sb<<endl;
72 }
View Code

6.P2463 [SDOI2008] Sandy 的卡片

差分一下,然后就变成所有串的 Lcp 和 3. 本质相同

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=2e6+3;
 6 int n,B,ok,col[N],cnt[N],lg[N],st[N][23];
 7 int m=4000,X[N],Y[N],c[N],sa[N],height[N],rk[N],ch[N];
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(n==p)return;m=p;
28     }
29 }
30 void Height()
31 {
32     int k=0;
33     for(int i=1;i<=n;i++)rk[sa[i]]=i;
34     for(int i=1;i<=n;i++)
35     {
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(i+k<=n&&j+k<=n&&ch[i+k]==ch[j+k])k++;
40         height[rk[i]]=k;
41     }
42 }
43 void Add(int x)
44 {
45     int y=col[x];
46     if(!y)return;
47     if(!cnt[y])ok++;
48     cnt[y]++;
49 }
50 void Del(int x)
51 {
52     int y=col[x];
53     if(!y)return;
54     cnt[y]--;
55     if(!cnt[y])ok--; 
56 }
57 void St()
58 {
59     for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
60     for(int i=1;i<=n;i++)st[i][0]=height[i];
61     for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
62         st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
63 }
64 int Ask(int l,int r)
65 {
66     int d=lg[r-l+1];
67     return min(st[l][d],st[r-(1<<d)+1][d]); 
68 }
69 int main()
70 {
71     cin>>B;
72     for(int t=1,z,x;t<=B;t++)
73     {
74         cin>>z>>x;
75         for(int i=1,y;i<z;i++)cin>>y,ch[++n]=y-x+2000,col[n]=t,x=y;
76         ch[++n]=++m;
77     }
78     n--;Sa();Height();St();
79     int i=0;
80     while(ok<B&&i<n)i++,Add(sa[i]); 
81     if(ok<B){cout<<1;return 0;}
82     int ans=Ask(2,i); 
83     for(int j=1;i<=n&&j<=n;)
84     {
85         Del(sa[j]),j++;
86         while(ok<B&&i<n)i++,Add(sa[i]);
87         if(ok<B){cout<<ans+1;return 0;}
88         ans=max(ans,Ask(j+1,i));
89     }
90     cout<<ans+1;
91 }
View Code

7.P4051 [JSOI2007] 字符加密

倍长一边串串,然后 SA 即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+3;
 6 int n,m=200,X[N],Y[N],c[N],sa[N],height[N],rk[N];
 7 char ch[N];
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(n==p)return;m=p;
28     }
29 }
30 int main()
31 {
32     cin>>(ch+1);n=strlen(ch+1);
33     for(int i=1;i<=n;i++)ch[i+n]=ch[i];
34     n*=2;Sa();
35     for(int i=1;i<=n;i++)if(sa[i]<=n/2)cout<<ch[sa[i]+n/2-1];
36 }
View Code

8.P4070 [SDOI2016] 生成魔咒

开始上强度。发现每次向后插入字符时,一切都变得神秘。

正难则反,反转字符串,每次插入一个前缀只会影响自己。提前跑出 height,发现每次的贡献就是加上自己和前驱,自己和后继,减去前驱和后继的贡献,维护一个 set 即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+3;
 6 ll n,m,a[N],X[N],Y[N],c[N],sa[N],height[N],rk[N],lg[N],st[N][23];
 7 map<ll,ll>mp;set<ll>q;
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=a[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(p==n)return;m=p;
28     }
29 }
30 void Height()
31 {
32     ll k=0;
33     for(int i=1;i<=n;i++)rk[sa[i]]=i;
34     for(int i=1;i<=n;i++)
35     {
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k])k++;
40         height[rk[i]]=k;
41     }
42 }
43 void St()
44 {
45     for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
46     for(int i=1;i<=n;i++)st[i][0]=height[i];
47     for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
48         st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
49 }
50 ll Ask(int l,int r)
51 {
52     int d=lg[r-l+1];
53     return min(st[l][d],st[r-(1<<d)+1][d]); 
54 }
55 int main()
56 {
57     cin>>n;ll ans=0;
58     for(int i=1;i<=n;i++)cin>>a[i],mp[a[i]]=0;
59     for(auto &p:mp)p.second=++m;
60     for(int i=1;i<=n;i++)a[i]=mp[a[i]];
61     reverse(a+1,a+n+1);Sa();Height();St();
62     for(int i=n;i>=1;i--)
63     {
64         int za=0,zb=0,zc=rk[i];
65         q.insert(zc);auto it=q.find(zc);
66         if(it!=q.begin())za=*(--it),it++;
67         if(++it!=q.end())zb=*it;
68         if(za)ans-=Ask(za+1,zc);
69         if(zb)ans-=Ask(zc+1,zb);
70         if(za&&zb)ans+=Ask(za+1,zb);
71         ans+=n-i+1;cout<<ans<<endl;
72     }
73 }
View Code

9.P2178 [NOI2015] 品酒大会

搞出height,从大到小暴力并查集合并一下,维护最大值以及方案数即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+5;
 6 ll n,m=200,a[N],X[N],Y[N],c[N],sa[N],height[N],rk[N],f[N],sz[N],mx[N],mn[N],ans1[N],ans2[N],sx,sy=(ll)-1e18;
 7 char ch[N];
 8 vector<int>e[N];
 9 void Sa()
10 {    
11     for(int i=1;i<=n;i++)X[i]=ch[i],c[X[i]]++;
12     for(int i=2;i<=m;i++)c[i]+=c[i-1];
13     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
14     for(int k=1,p=0;k<=n;k=k<<1,p=0)
15     {
16         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
17         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
18         for(int i=1;i<=m;i++)c[i]=0;
19         for(int i=1;i<=n;i++)c[X[i]]++;
20         for(int i=2;i<=m;i++)c[i]+=c[i-1];
21         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
22         swap(X,Y);p=1;X[sa[1]]=1;
23         for(int i=2;i<=n;i++)
24         {
25             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
26             else X[sa[i]]=++p;
27         }
28         if(p==n)return;m=p;
29     }
30 }
31 int Height()
32 {
33     int k=0;
34     for(int i=1;i<=n;i++)rk[sa[i]]=i;
35     for(int i=1;i<=n;i++)
36     {
37         if(rk[i]==1)continue; 
38         if(k)k--;
39         int j=sa[rk[i]-1];
40         while(j+k<=n&&i+k<=n&&ch[i+k]==ch[j+k])k++;
41         height[rk[i]]=k;
42     }
43 }
44 int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
45 void Merge(int x,int y)
46 {
47     x=F(x);y=F(y);sx+=sz[x]*sz[y];sy=max(sy,max(mx[x]*mx[y],mn[x]*mn[y]));
48     f[y]=x;sz[x]+=sz[y];mx[x]=max(mx[x],mx[y]);mn[x]=min(mn[x],mn[y]);
49 }
50 int main()
51 {
52     cin>>n;
53     for(int i=1;i<=n;i++)cin>>ch[i];
54     for(int i=1;i<=n;i++)cin>>a[i];
55     Sa();Height();
56     for(int i=1;i<=n;i++)f[i]=i,sz[i]=1,mx[i]=mn[i]=a[sa[i]];
57     for(int i=2;i<=n;i++)e[height[i]].push_back(i);
58     for(int i=n-1;i>=0;i--)
59     {
60         for(int j:e[i])Merge(j,j-1);
61         if(sx)ans1[i]=sx,ans2[i]=sy;
62     }
63     for(int i=0;i<n;i++)cout<<ans1[i]<<" "<<ans2[i]<<endl;
64     return 0;
65 }
View Code

10.P4081 [USACO17DEC] Standing Out from the Herd P

对于每个后缀,要减去 $max$ 和来自上一个来自同一个串的lcp,来自不同串串的lcp。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int N=1e6+3;
 6 ll n,m=200,B,a[N],X[N],Y[N],c[N],sa[N],height[N],rk[N],lg[N],st[N][23],col[N],sx[N],ans[N];
 7 char ca[N];
 8 void Sa()
 9 {
10     for(int i=1;i<=n;i++)X[i]=a[i],c[X[i]]++;
11     for(int i=2;i<=m;i++)c[i]+=c[i-1];
12     for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
13     for(int k=1,p=0;k<=n;k=k<<1,p=0)
14     {
15         for(int i=n-k+1;i<=n;i++)Y[++p]=i;
16         for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
17         for(int i=1;i<=m;i++)c[i]=0;
18         for(int i=1;i<=n;i++)c[X[i]]++;
19         for(int i=2;i<=m;i++)c[i]+=c[i-1];
20         for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
21         swap(X,Y);p=1;X[sa[1]]=1;
22         for(int i=2;i<=n;i++)
23         {
24             if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
25             else X[sa[i]]=++p;
26         }
27         if(n==p)return;m=p;
28     }
29 }
30 void Height()
31 {
32     ll k=0;
33     for(int i=1;i<=n;i++)rk[sa[i]]=i;
34     for(int i=1;i<=n;i++)
35     {
36         if(rk[i]==1)continue;
37         if(k)k--;
38         int j=sa[rk[i]-1];
39         while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k])k++;
40         height[rk[i]]=k; 
41     }
42 }
43 void St()
44 {
45     for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
46     for(int i=1;i<=n;i++)st[i][0]=height[i];
47     for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
48         st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
49 }
50 int Lcp(int l,int r)
51 {
52     int d=lg[r-l+1];
53     return min(st[l][d],st[r-(1<<d)+1][d]);
54 }
55 int main()
56 {
57     cin>>B;
58     for(int t=1;t<=B;t++)
59     {
60         cin>>(ca+1);ll z=strlen(ca+1);ans[t]=z*(z+1)/2;
61         for(int i=1;i<=z;i++)a[++n]=ca[i],col[n]=t;
62         if(t<B)a[++n]=++m;
63     }
64     Sa();Height();St();ll la=0,lb=0;
65     for(int i=n;i>=1;i--)
66     {
67         if(!col[sa[i]])continue;
68         if(!la){la=i;continue;}
69         if(col[sa[i]]==col[sa[la]]&&!lb)continue;
70         if(col[sa[i]]!=col[sa[la]])sx[i]=Lcp(i+1,la),lb=la,la=i;
71         else sx[i]=Lcp(i+1,lb),la=i;
72     }
73     for(int i=1;i<=n;i++)ans[col[sa[i]]]-=max(sx[i],height[i]);
74     for(int i=1;i<=B;i++)cout<<ans[i]<<endl;
75 }
View Code

 

posted @ 2023-08-01 21:27  Hanghang007  阅读(36)  评论(0)    收藏  举报