1. 程式人生 > >BZOJ_3238_[Ahoi2013]差異_後綴自動機

BZOJ_3238_[Ahoi2013]差異_後綴自動機

esc mes https #define lld style char s 發現 組成

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);
}

BZOJ_3238_[Ahoi2013]差異_後綴自動機