BZOJ 3238 [Ahoi2013]差异(后缀自动机)
【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3238
【题目大意】
给出一个串,设T[i]表示从第i位开始的后缀,
求sum(len(T[i])+len(T[j])-2*lcp(T[i],T[j]))
【题解】
根据反串的后缀自动机建立后缀树,
则两点的LCA在自动机中的length就是他们的LCP,
树形DP统计一下即可。
【代码】
#include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; const int N=1000010; struct SAM{ char s[N]; int p,q,np,nq,cnt,lst,a[N][26],l[N],f[N],size[N],tot; int Tr(char c){return c-'a';} SAM(){cnt=0;lst=++cnt;} void extend(int c){ p=lst;np=lst=++cnt;l[np]=l[p]+1;size[np]=1; while(!a[p][c]&&p)a[p][c]=np,p=f[p]; if(!p)f[np]=1; else{ q=a[p][c]; if(l[p]+1==l[q])f[np]=q; else{ nq=++cnt;l[nq]=l[p]+1; memcpy(a[nq],a[q],sizeof(a[q])); f[nq]=f[q]; f[np]=f[q]=nq; while(a[p][c]==q)a[p][c]=nq,p=f[p]; } } } vector<int> v[N]; void BuildTree(){ scanf("%s",s+1); int len=strlen(s+1); for(int i=len;i;i--)extend(Tr(s[i])); for(int i=2;i<=cnt;i++)v[f[i]].push_back(i); } long long res; void Dfs(int x,int fx){ for(int i=0;i<v[x].size();i++){ int y=v[x][i]; Dfs(y,x); size[x]+=size[y]; }l[x]-=l[fx]; res=res-(long long)size[x]*(size[x]-1)*l[x]; } void ShowResult(){ int len=strlen(s+1); res=(long long)(len-1)*len*(len+1)/2; for(int i=0;i<v[1].size();i++)Dfs(v[1][i],1); printf("%lld\n",res); } }sam; int main(){ sam.BuildTree(); sam.ShowResult(); return 0; }
愿你出走半生,归来仍是少年