BZOJ 3238 [Ahoi2013]差异

后缀自动机+后缀树

有一种不难想到的后缀数组的做法,详见黄学长题解

然而我做这题的目的主要是熟悉一下后缀自动机和后缀树- -

一个有趣的性质:反串的后缀自动机的parent树就是正串的后缀树,其树上的边权长度=parent树上儿子的len-父亲的len

构出后缀树之后就是一个小DP啦。

#include<cstdio>
#include<cstring> 
#define N 500010
#define S 28
using namespace std;
namespace ziqian
{
    typedef long long ll;
    char s[N];
    ll ans = 0;
    int last[N*2], ecnt, siz[N*2]; struct edge{int next, to;}e[N<<1];
    void addedge(int a, int b){e[++ecnt] = (edge){last[a], b}; last[a] = ecnt;}
    struct SAM
    {
        SAM *par, *ch[S];
        int len;
    }mem[N*2], *tot, *null, *tai, *root;
    SAM *newSAM(int l)
    {
        SAM *r = ++tot;
        *r = *null; 
        r->len = l;
        return r;
    }
    void init_SAM()
    {
        tot = mem;
        null = ++tot;
        null -> par = null;
        for(int i = 0; i < S; i++) null->ch[i] = null;
        null->len = 0;
        root = newSAM(0); 
        tai = root;
    }
    void extend(int v)
    {
        SAM *p = tai, *np = newSAM(p->len + 1); tai = np; siz[np - mem] = 1; 
        for(; p != null && p->ch[v] == null; p = p->par) p->ch[v] = np;
        if(p == null) np -> par = root;
        else
        {
            SAM *q = p->ch[v];
            if(p->len + 1 == q->len) np->par = q;
            else
            {
                SAM *nq = newSAM(p->len + 1);
                *nq = *q;
                nq -> len = p->len + 1;
                q->par = np->par = nq;
                for(; p != null && p->ch[v] == q; p = p->par) p->ch[v] = nq; 
            }
        }
    }
    void dp(int x)
    {
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to;
            dp(y);
            ans -= 2ll * siz[x] * siz[y] * (mem+x)->len;
            siz[x] += siz[y];
        }
    }
    void main()
    {
        scanf("%s",s+1);
        int n = strlen(s+1);
        init_SAM();
        for(int i = n; i; i--) extend(s[i] - 'a');
        for(int i = 1; i <= n; i++) ans += (ll) i * (n-1);
        for(SAM *cur = tot; cur != mem; cur--)
            addedge(cur->par - mem, cur - mem);
        dp(root - mem);
        printf("%lld\n",ans);
    }
}
int main()
{
    ziqian::main();
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值