bzoj 3238 [Ahoi2013]差异(SAM解法)

来源:互联网 发布:淘宝分销中心在哪里 编辑:程序博客网 时间:2024/04/29 21:25

3238: [Ahoi2013]差异

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 3113  Solved: 1406
[Submit][Status][Discuss]

Description

Input

一行,一个字符串S

Output

 

一行,一个整数,表示所求值

Sample Input

cacao

Sample Output


54

HINT



2<=N<=500000,S由小写英文字母组成




【分析】

后缀自动机性质:两个串的最长公共后缀,位于这两个串对应状态在parent树上的LCA状态的深度

因为对于一个串,它的pre一定是它的一个后缀,而且深度越大后缀越长。

所以我们把原串反过来,变成了求两两前缀的最长公共后缀之和。

把SAM最长链标记一下,构建出来parent树,DP一下即可

也可以参考 陈立杰论文




【代码】

//bzoj 1396 识别子串#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#define inf 1e9+7#define ll long long#define M(a) memset(a,0,sizeof a)#define fo(i,j,k) for(i=j;i<=k;i++)using namespace std;const int mxn=1000000;ll ans; char S[mxn],s[mxn];int n,m,p,q,np,nq,len,tot,root,cnt;int mark[mxn],t[mxn],b[mxn],head[mxn];int step[mxn],pre[mxn],son[mxn][30];struct edge {int to,next;}f[mxn];inline void add(int u,int v){f[++cnt].to=v,f[cnt].next=head[u],head[u]=cnt;}inline void sam(){int i,j;scanf("%s",S+1);len=strlen(S+1);fo(i,1,len) s[i]=S[len-i+1];ans=(ll)(len-1)*(ll)len*(ll)(len+1)/2;root=tot=np=1;fo(i,1,len){int c=s[i]-'a'+1;p=np;step[np=(++tot)]=step[p]+1;while(p && !son[p][c])  son[p][c]=np,p=pre[p];if(!p){pre[np]=root;continue;}q=son[p][c];if(step[q]==step[p]+1)  pre[np]=q;else{step[nq=(++tot)]=step[p]+1;memcpy(son[nq],son[q],sizeof son[q]);pre[nq]=pre[q];pre[q]=pre[np]=nq;while(p && son[p][c]==q)  son[p][c]=nq,p=pre[p];}}fo(i,1,tot) if(pre[i])  add(pre[i],i);p=root;fo(i,1,len){int c=s[i]-'a'+1;p=son[p][c],mark[p]++;}}inline void dfs(int u){for(int i=head[u];i;i=f[i].next){int v=f[i].to;dfs(v);ans-=(ll)2*(ll)step[u]*(ll)mark[u]*(ll)mark[v];mark[u]+=mark[v];}}int main(){int i,j;sam();dfs(1);printf("%lld\n",ans);return 0;}


0 0
原创粉丝点击