[BZOJ3238] [AHOI2013] 差异 - 后缀自动机

来源:互联网 发布:微博 知乎 编辑:程序博客网 时间:2024/06/11 03:57

3238: [Ahoi2013]差异

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1968  Solved: 896
[Submit][Status][Discuss]

Description

Input

一行,一个字符串S

Output

 

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

Sample Input

cacao

Sample Output


54

HINT



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

Source

[Submit][Status][Discuss]

感觉窝会求后缀数组然后完全不会用……

好伐我还是写了SAM2333333。

显然题意只要我们求出∑LCP(i,j)(i!=j)就好

首先我们跑一遍SAM求出后缀树,然后拓扑一下就能求出所有right集合的大小。

显然两个后缀的最长公共前缀长就是他们在后缀树上的lca的深度。

我们可以考虑对每个深度进行分段,即点x对答案的贡献只包含其在后缀树上的父节点的深度以上且在自身深度以下的答案。

显然这样做的答案就是∑deep[i]*∑right[i]-right[son[i]]

又因为right[i]=∑right[son[i]]

那么每个点的贡献就是deep[i]*right[i]*right[i-1]了

#include"bits/stdc++.h"using namespace std;typedef long long ll; const int N=1000005,A=26;struct SAM{    char s[N];int t,l,q[N],last,a[N];    int fail[N],e[N][A],v[N],d[N];    SAM(){t=last=1;}    void extend(int c){        int np=++t,p=last;        d[np]=d[p]+1,v[last=np]=1;        for(;!e[p][c]&&p;p=fail[p])            e[p][c]=np;        if(!p)fail[np]=1;        else {            int q=e[p][c],nq;            if(d[q]!=d[p]+1){                nq=++t;d[nq]=d[p]+1;                memcpy(e[nq],e[q],sizeof(int[A]));                fail[nq]=fail[q];fail[np]=fail[q]=nq;                for(;e[p][c]==q;p=fail[p])e[p][c]=nq;            } else fail[np]=q;        }    }    void read(){scanf("%s",s);l=strlen(s);}    void build(){for(int i=0;i<l;i++)extend(s[i]-'a');}    ll getans(){        for(int i=1;i<=t;i++)a[d[i]]++;        for(int i=1;i<=l;i++)a[i]+=a[i-1];        for(int i=t;i;i--)q[a[d[i]]--]=i;        for(int i=t;i;i--)v[fail[q[i]]]+=v[q[i]];        v[1]=v[0]=0;ll ret=(ll)(l-1)*l*(l+1)/2;        for(int i=2;i<=t;i++)ret-=(ll)v[i]*(v[i]-1)*(d[i]-d[fail[i]]);        return ret;    }} sam; int main(){    sam.read();sam.build();    printf("%lld\n",sam.getans());    return 0; }


0 0
原创粉丝点击