这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
2020-2021:teams:legal_string:jxm2001:字符串_3 [2020/08/31 12:47] jxm2001 |
2020-2021:teams:legal_string:jxm2001:字符串_3 [2020/09/02 10:52] (当前版本) jxm2001 |
||
|---|---|---|---|
| 行 1: | 行 1: | ||
| ====== 字符串 3 ====== | ====== 字符串 3 ====== | ||
| - | ===== 后缀数组 ===== | + | ===== 后缀数组(SA) ===== |
| ==== 算法简介 ==== | ==== 算法简介 ==== | ||
| 行 41: | 行 41: | ||
| <code cpp> | <code cpp> | ||
| namespace SA{ | namespace SA{ | ||
| - | int sa[MAXN],rk[MAXN],height[MAXN],x[MAXN],y[MAXN],c[MAXN]; | + | int sa[MAXN],rk[MAXN],height[MAXN],X[MAXN],Y[MAXN],c[MAXN]; |
| int d[MAXN][MAXM],lg2[MAXN]; | int d[MAXN][MAXM],lg2[MAXN]; | ||
| void get_sa(char *s,int n,int m){//s下标从1开始 | void get_sa(char *s,int n,int m){//s下标从1开始 | ||
| + | int *x=X,*y=Y; | ||
| _rep(i,0,m)c[i]=0; | _rep(i,0,m)c[i]=0; | ||
| _rep(i,1,n)c[x[i]=s[i]]++; | _rep(i,1,n)c[x[i]=s[i]]++; | ||
| 行 577: | 行 578: | ||
| $$\sum_{1\le i\lt j\le n}|T_i|+|T_j|-2\times \text{LCP}(T_i,T_j)=(n-1)\sum_{i=1}^n|T_i|-2\times \sum_{1\le i\lt j\le n}\text{LCP}(T_i,T_j)=\frac {(n-1)n(n+1)}2-2\times \sum_{1\le i\lt j\le n}\text{LCP}(T_i,T_j)$$ | $$\sum_{1\le i\lt j\le n}|T_i|+|T_j|-2\times \text{LCP}(T_i,T_j)=(n-1)\sum_{i=1}^n|T_i|-2\times \sum_{1\le i\lt j\le n}\text{LCP}(T_i,T_j)=\frac {(n-1)n(n+1)}2-2\times \sum_{1\le i\lt j\le n}\text{LCP}(T_i,T_j)$$ | ||
| + | |||
| + | 发现只要维护后面部分即可。 | ||
| + | |||
| + | 考虑单调栈维护,设 $\text{dp}_i=\sum_{1\le j\lt i}\text{LCP}(T_i,T_j)$,用单调栈维护 $height_i$ 数组。 | ||
| + | |||
| + | 于是对所有 $i$,可以均摊 $O(1)$ 得到最大的 $j\lt i$ 满足 $\text{hieght}_j\le \text{height}_i$。 | ||
| + | |||
| + | 根据 $\text{height}$ 性质,发现 $j\lt k\le i$ 时 $\text{LCP}(T_k,T_i)=\text{height}_i$,而 $k\le j$ 时 $\text{LCP}(T_k,T_i)=\text{LCP}(T_k,T_j)$。 | ||
| + | |||
| + | 于是 $\text{dp}_i=\sum_{1\le j\lt i}\text{LCP}(T_i,T_j)=\text{dp}_j+(i-j)\text{height}_i$。 | ||
| + | |||
| + | 另外该题也可以用笛卡尔树或并查集等维护。 | ||
| + | |||
| + | <hidden 查看代码> | ||
| + | <code cpp> | ||
| + | const int MAXN=5e5+5,MAXM=20; | ||
| + | namespace SA{ | ||
| + | int sa[MAXN],rk[MAXN],height[MAXN],x[MAXN],y[MAXN],c[MAXN]; | ||
| + | void get_sa(char *s,int n,int m){ | ||
| + | _rep(i,0,m)c[i]=0; | ||
| + | _rep(i,1,n)c[x[i]=s[i]]++; | ||
| + | _rep(i,1,m)c[i]+=c[i-1]; | ||
| + | for(int i=n;i;i--)sa[c[x[i]]--]=i; | ||
| + | for(int k=1;k<n;k<<=1){ | ||
| + | int pos=0; | ||
| + | _rep(i,n-k+1,n)y[++pos]=i; | ||
| + | _rep(i,1,n)if(sa[i]>k)y[++pos]=sa[i]-k; | ||
| + | _rep(i,0,m)c[i]=0; | ||
| + | _rep(i,1,n)c[x[i]]++; | ||
| + | _rep(i,1,m)c[i]+=c[i-1]; | ||
| + | for(int i=n;i;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0; | ||
| + | swap(x,y); | ||
| + | pos=0,y[n+1]=0; | ||
| + | _rep(i,1,n)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?pos:++pos; | ||
| + | if(pos==n)break; | ||
| + | m=pos; | ||
| + | } | ||
| + | _rep(i,1,n)rk[sa[i]]=i; | ||
| + | } | ||
| + | void get_height(char *s,int n){ | ||
| + | for(int i=1,k=0;i<=n;i++){ | ||
| + | if(k)k--; | ||
| + | while(s[i+k]==s[sa[rk[i]-1]+k])k++; | ||
| + | height[rk[i]]=k; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | char buf[MAXN]; | ||
| + | pair<int,int> Stack[MAXN]; | ||
| + | LL dp[MAXN]; | ||
| + | int main() | ||
| + | { | ||
| + | scanf("%s",buf+1); | ||
| + | int n=strlen(buf+1); | ||
| + | SA::get_sa(buf,n,'z'); | ||
| + | SA::get_height(buf,n); | ||
| + | LL ans=1LL*(n-1)*n*(n+1)/2; | ||
| + | int top=0; | ||
| + | Stack[0]=make_pair(0,0); | ||
| + | _rep(i,1,n){ | ||
| + | while(top&&Stack[top].second>SA::height[i])top--; | ||
| + | dp[i]=dp[Stack[top].first]+1LL*(i-Stack[top].first)*SA::height[i],ans-=dp[i]*2; | ||
| + | Stack[++top]=make_pair(i,SA::height[i]); | ||
| + | } | ||
| + | enter(ans); | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||