BZOJ_3238_[Ahoi2013]差异_后缀自动机

BZOJ_3238_[Ahoi2013]差异_后缀自动机

Description

Input

一行,一个字符串S

Output

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

Sample Input

cacao

Sample Output

54

HINT

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


 

后缀数组做法:http://www.cnblogs.com/suika/p/8995997.html

 

可以发现两个后缀的lcp长度一定是这两个串在后缀树上的lca的深度。

对后缀树上每个结点维护子树中叶子个数,然后向上走的时候统计一下有多少后缀两两的LCA在当前结点上即可。

 

代码:

 

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 500050
int ch[N<<1][27],fa[N<<1],dep[N<<1],cnt=1,lst=1,siz[N<<1];
char s[N];
int c[N<<1],a[N<<1];
void insert(int x) {
    int p=lst,np=++cnt,q,nq;
    lst=np; dep[np]=dep[p]+1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
    if(!p) fa[np]=1;
    else {
        q=ch[p][x];
        if(dep[q]==dep[p]+1) fa[np]=q;
        else {
            fa[nq=++cnt]=fa[q];
            dep[nq]=dep[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[q]=fa[np]=nq;
            for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        } 
    }
    siz[np]=1;
}
int main() {
    scanf("%s",s+1);
    int ln=strlen(s+1);
    int i;
    long long ans=0;
    for(i=ln;i;i--) insert(s[i]-'a');
    for(i=1;i<=cnt;i++) c[dep[i]]++;
    for(i=1;i<=cnt;i++) c[i]+=c[i-1];
    for(i=1;i<=cnt;i++) a[c[dep[i]]--]=i;
    for(i=cnt;i;i--) {
        int p=a[i];
        ans+=1ll*siz[fa[p]]*siz[p]*dep[fa[p]];
        siz[fa[p]]+=siz[p];
    }
    printf("%lld\n",1ll*ln*(ln-1)*(ln+1)/2-2*ans);
}

 

posted @ 2018-05-13 20:12  fcwww  阅读(222)  评论(0编辑  收藏  举报