SA-题目
SA的题目
题意概述:给定一个长度为 $n$ 的字符串 $S$,令 $T_ i$ 表示它从第 $i$ 个字符开始的后缀。$2<=N<=50000$。求$$\sum_{1<=i<j<=n}len(T_i)+len(T_j)-2 \times lcp(T_i,T_j)$$
通过观察可以发现,每个后缀的长度被计算的次数是 $n-1$ ,而所有后缀长度的和是一个等差数列,$\frac{n\times(n+1)}{2}$,所以式子的前半部分就是 $\frac{(n^3-n)}{2}$ 。
现在来求后半部分,等价于所有后缀对的 $lcp$ 之和再乘二,看到后缀,我就想到 $SA$,看到 $lcp$ ,我就想到求 $height$ 数组,求出 $height$ 数组,原问题就转化为整个序列中(每个区间的最小值)之和。
这个问题可以用单调栈扫两遍,找到每个点可以作为最小值的左右端点,并注意一下边界情况,有相同最小值的时候不要算重复就可以通过了。
对于这种问题还有另一个很不错的方法:烜式合并;这是烜神仙的 $blog$ :https://www.cnblogs.com/asuldb/p/10205640.html
1 # include <cstdio> 2 # include <iostream> 3 # include <cstring> 4 # include <string> 5 # define R register int 6 # define ll long long 7 8 using namespace std; 9 10 const int maxn=500005; 11 char c[maxn]; 12 int n,sa[maxn],ht[maxn],ta[maxn],tb[maxn],A[maxn],B[maxn],rk[maxn],m,l[maxn],r[maxn],Tp,k[maxn],v[maxn]; 13 ll ans,anss; 14 15 inline void build_SA() 16 { 17 for (R i=1;i<=n;++i) ta[ c[i] ]++; 18 for (R i=1;i<=200;++i) ta[i]+=ta[i-1]; 19 for (R i=n;i>=1;--i) sa[ ta[ c[i] ]-- ]=i; 20 rk[ sa[1] ]=1; 21 for (R i=2;i<=n;++i) rk[ sa[i] ]=(c[ sa[i] ]==c[ sa[i-1] ])?rk[ sa[i-1] ]:rk[ sa[i-1] ]+1; 22 for (R l=1;rk[ sa[n] ]<n;l<<=1) 23 { 24 for (R i=1;i<=n;++i) ta[i]=tb[i]=0; 25 for (R i=1;i<=n;++i) 26 { 27 ta[ A[i]=rk[i] ]++; 28 tb[ B[i]=(i+l<=n)?rk[i+l]:0 ]++; 29 } 30 for (R i=1;i<=n;++i) ta[i]+=ta[i-1],tb[i]+=tb[i-1]; 31 for (R i=n;i>=1;--i) rk[ tb[ B[i] ]-- ]=i; 32 for (R i=n;i>=1;--i) sa[ ta[ A[ rk[i] ] ]-- ]=rk[i]; 33 rk[ sa[1] ]=1; 34 for (R i=2;i<=n;++i) 35 { 36 rk[ sa[i] ]=rk[ sa[i-1] ]; 37 if(A[ sa[i] ]!=A[ sa[i-1] ]||B[ sa[i] ]!=B[ sa[i-1] ]) rk[ sa[i] ]++; 38 } 39 } 40 int j=0; 41 for (R i=1;i<=n;++i) 42 { 43 if(j) j--; 44 while (c[ i+j ]==c[ sa[ rk[i]-1 ]+j ]) j++; 45 ht[ rk[i] ]=j; 46 } 47 } 48 49 int main() 50 { 51 scanf("%s",c+1); 52 n=strlen(c+1); 53 ans=1LL*n*(n+1)/2*(n-1); 54 build_SA(); 55 for (R i=1;i<=n;++i) 56 { 57 while(Tp&&ht[i]<v[Tp]) r[ k[Tp] ]=i-k[Tp],Tp--; 58 k[++Tp]=i,v[Tp]=ht[i]; 59 } 60 for (R i=1;i<=Tp;++i) r[ k[i] ]=n-k[i]+1; 61 Tp=0; 62 for (R i=n;i>=1;--i) 63 { 64 while(Tp&&ht[i]<=v[Tp]) l[ k[Tp] ]=k[Tp]-i,Tp--; 65 k[++Tp]=i,v[Tp]=ht[i]; 66 } 67 for (R i=1;i<=Tp;++i) l[ k[i] ]=k[i]-1; 68 for (R i=1;i<=n;++i) anss+=1LL*l[i]*r[i]*ht[i]; 69 printf("%lld",ans-anss*2); 70 return 0; 71 }