• 【BZOJ 3238】差异 后缀自动机+树形DP


    题意

    给定字符串,令$s_i$表示第$i$位开始的后缀,求$sum_{1le i < j le n} len(s_i)+len(s_j)-2 imes lcp(s_i,s_j)$


    先考虑前面的和式,直接计算为$frac{n(n^2-1)}{2}$,考虑后面的和式,$lcp$相关可以用sam求解,sam形成的parent树是原串的前缀树,所以两个串的最长公共后缀是在parent树上最近公共祖先对应的状态的长度$maxlen_s-maxlen_{pa_s}$,将原串反向建立sam得到后缀树,parent树上每个状态的子串个数为$Right_s$,每个状态的贡献为$2 imes inom{Right_s}{2} imes (maxlen_s-maxlen_{pa_s})$,在parent树上跑一遍dp即可求出

    时间复杂度$O(n)$

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 1001000;
    int trans[N][30], pa[N], maxlen[N], sz, root, last, Right[N];
    inline void init_sam() {
        memset(trans, 0, sizeof(trans));
        root = last = sz = 1;
    }
    inline void extend(int c, int x) {
        int p = last, np = ++sz; last = np; maxlen[np] = x; Right[np] = 1;
        for(; p && !trans[p][c]; p = pa[p]) trans[p][c] = np;
        if(!p) {pa[np] = root; return;}
        int q = trans[p][c];
        if(maxlen[q] == maxlen[p] + 1) {
            pa[np] = q;
        }else {
            int nq = ++sz;
            memcpy(trans[nq], trans[q], sizeof(trans[q]));
            pa[nq] = pa[q]; maxlen[nq] = maxlen[p] + 1; pa[q] = pa[np] = nq;
            for(; trans[p][c] == q; p = pa[p]) trans[p][c] = nq;
        }
    }
    inline void build(char *s) {
        int len = strlen(s);
        for(int i = 0; i < len; ++i) extend(s[i] - 'a', i + 1);
    }
    int cnt, head[N], nxt[N], to[N];
    inline void init_edge() {cnt = 0; memset(head, -1, sizeof(head));}
    inline void add(int u, int v) {to[cnt] = v; nxt[cnt] = head[u]; head[u] = cnt++;}
    LL ans = 0;
    int dfs(int u) {
        for(int i = head[u]; ~i; i = nxt[i]) Right[u] += dfs(to[i]);
        ans -= 1LL * Right[u] * (Right[u] - 1) * (maxlen[u] - maxlen[pa[u]]);
        return Right[u];
    }
    inline void get() {
        init_edge();
        for(int i = 2; i <= sz; ++i) add(pa[i], i);
        dfs(root);
    }
    char str[N];
    int main() {
        scanf("%s", str);
        int len = strlen(str);
        reverse(str, str + len);
        init_sam();
        build(str);
        ans = 1LL * len * (len - 1) * (len + 1) / 2;
        get();
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    linux 串口调试总结
    linux 环境变量的设置方法
    linux 内核的配置和编译
    linux 串口应用程序的编写指南
    linuxdifff 和 patch 的使用指南
    linux shell 的学习随笔
    特性阻抗和频率有关吗?
    linux source和export命令的使用
    关于SI设计与SI仿真的一点浅见
    LINUX下的tty,console与串口分析
  • 原文地址:https://www.cnblogs.com/ogiso-setsuna/p/8497095.html
Copyright © 2020-2023  润新知