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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值