http://www.lydsy.com/JudgeOnline/problem.php?id=4199
给定一个字符串S,设Si表示S从i开始的的后缀。若Si, Sj存在长度为r的公共前缀,则称i和j是r相似的。每个后缀有一个价值vi,一对后缀Si与Sj的价值是vi*vj。对于每个r=0~n-1,统计有多少对r相似的后缀,以及r相似的后缀的最大价值(n≤300000)。
我们先后缀排序,并得到height值。我们考虑动态维护集合,保证集合内的答案已经统计过了,在合并的时候更新集合与集合间的答案即可。初始每个后缀自己构成一个大小为1的集合。按height从大到小枚举每个后缀,将rank为i的后缀与rank为i-1的后缀合并,他们更新的r即为当前的height。更新答案并用并查集维护集合即可。
#include<bits/stdc++.h> using namespace std; const int maxn=300015; typedef long long int64; typedef pair<int,int> PII; int n,v[maxn];char s[maxn]; struct Tsuffix_array{ int sum[maxn],sa[maxn],rank[maxn],tsa[maxn],trank[maxn]; bool cmp(int i,int j,int l){ if (i+l>n||j+l>n) return 0; return rank[i]==rank[j]&&rank[i+l]==rank[j+l]; } void suffix_sort(){ int m=255,p,i,j; for (i=0;i<=m;++i) sum[i]=0; for (i=1;i<=n;++i) ++sum[rank[i]=s[i]]; for (i=1;i<=m;++i) sum[i]+=sum[i-1]; for (i=n;i>=1;--i) sa[sum[rank[i]]--]=i; for (j=1,p=0;p<n;j<<=1,m=p){ for (p=0,i=n-j+1;i<=n;++i) tsa[++p]=i; for (i=1;i<=n;++i) if (sa[i]>j) tsa[++p]=sa[i]-j; for (i=0;i<=m;++i) sum[i]=0; for (i=1;i<=n;++i) ++sum[rank[tsa[i]]]; for (i=1;i<=m;++i) sum[i]+=sum[i-1]; for (i=n;i>=1;--i) sa[sum[rank[tsa[i]]]--]=tsa[i]; for (p=trank[sa[1]]=1,i=2;i<=n;++i) trank[sa[i]]=cmp(sa[i],sa[i-1],j)?p:++p; memcpy(rank,trank,sizeof(int)*(n+1)); } } int height[maxn]; void get_height(){ for (int h=0,i=1;i<=n;++i){ if (rank[i]==1) continue; for (h?--h:0;s[i+h]==s[sa[rank[i]-1]+h];++h); height[rank[i]]=h; } } int fa[maxn],siz[maxn],fmx[maxn],fmn[maxn]; int find(int u){return u==fa[u]?u:fa[u]=find(fa[u]);} void merge(int x,int y){ if (siz[x]<siz[y]) swap(x,y); fa[y]=x;siz[x]+=siz[y]; fmx[x]=max(fmx[x],fmx[y]); fmn[x]=min(fmn[x],fmn[y]); } PII t[maxn]; int64 ans1[maxn],ans2[maxn]; void get_ans(){ for (int i=2;i<=n;++i) t[i-1]=make_pair(height[i],i); for (int i=1;i<=n;++i) fmx[rank[i]]=fmn[rank[i]]=v[i]; sort(t+1,t+n,greater<PII>());memset(ans2,200,sizeof(ans2)); for (int i=1;i<=n;++i){fa[i]=i;siz[i]=1;} for (int i=1;i<=n-1;++i){ int x=find(t[i].second-1),y=find(t[i].second); ans1[t[i].first]+=1ll*siz[x]*siz[y]; ans2[t[i].first]=max(ans2[t[i].first],1ll*fmx[x]*fmx[y]); ans2[t[i].first]=max(ans2[t[i].first],1ll*fmn[x]*fmx[y]); ans2[t[i].first]=max(ans2[t[i].first],1ll*fmx[x]*fmn[y]); ans2[t[i].first]=max(ans2[t[i].first],1ll*fmn[x]*fmn[y]); merge(x,y); } for (int i=n-1;i>=0;--i){ ans1[i]+=ans1[i+1]; ans2[i]=max(ans2[i],ans2[i+1]); } for (int i=0;i<=n-1;++i) printf("%lld %lld ",ans1[i],!ans1[i]?0:ans2[i]); } }SA; void init(){ scanf("%d%s",&n,s+1); for (int i=1;i<=n;++i) scanf("%d",&v[i]); } void work(){ SA.suffix_sort(); SA.get_height(); SA.get_ans(); } int main(){ init(); work(); return 0; }