• [Note]后缀自动机


    后缀自动机

    代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    const int M = 1e6 + 10;
    const int N = 5e5 + 10;
    
    char s[N];
    int trns[M][26], pa[M], mxl[M], mnl[M], gr[M], ep[M];
    int len, n;
    int tmp[N], rnk[M];
    
    inline int nwst(int mx, int mn, int *tr, int sl) {
        mxl[n] = mx; mnl[n] = mn; pa[n] = sl;
        for (int i = 0; i < 26; ++i) {
            if (tr == NULL) trns[n][i]=-1;
            else trns[n][i] = tr[i];
        }
        return n++;
    }
    
    inline void link(int x, int y) {
        mnl[x] = mxl[y]+1; pa[x] = y; deg[y]++;
    }
    
    inline int addc(int c, int u) {
        int z = nwst(mxl[u]+1, -1, NULL, -1);
        gr[z] = 1;
        while (u != -1 && trns[u][c] == -1) {
            trns[u][c] = z;
            u = pa[u];
        }
        if (u == -1) {
            link(z, 0);
            return z;
        }
        int x = trns[u][c];
        if (mxl[x] == mxl[u]+1) {
            link(z, x);
            return z;
        }
        int y = nwst(mxl[u]+1, mnl[x], trns[x], pa[x]);
        link(x, y);
        link(z, y);
        while (u != -1 && trns[u][c] == x) {
            trns[u][c] = y;
            u = pa[u];
        }
        return z;
    }
    
    inline void build() {
        int u = nwst(0, 0, NULL, -1);
        for (int i = 0; i < len; ++i) u = addc(s[i]-'a', u);
        
        for (int i = 0; i < n; ++i) tmp[mxl[i]]++;
        for (int i = 1; i <= len; ++i) tmp[i] += tmp[i-1];
        for (int i = 0; i < n; ++i) rnk[tmp[mxl[i]]--] = i;
        for (int i = n; i; --i) {
            int &j = rnk[i];
            ep[j] += gr[j];
            if (pa[j] != -1) ep[pa[j]] += ep[j];
        }
        ep[0] = 0;
    }
    

    解释

    mxln : (maxlen)
    mnln : (minlen)
    ep : (|endpos|)
    trns : (trans)
    pa : (suffixLink)

    应用

    不同子串的数目问题

    (sum_{iin (0,n)} (maxlen_i-minlen_i+1))

    最多k长子串问题

    (|endpos|)即为某个节点所包含的串的出现次数。由于答案数组递减,只需要更新每个状态的(|maxlen|)对应的答案,最后再调整答案即可。

    for (int i = 1; i < n; ++i) 
        ans[mxln[i]] = std::max(ans[mxln[i]], ep[i]);
    for (int i = len-1; i; --i) 
        ans[i] = std::max(ans[i], ans[i+1]);
    
  • 相关阅读:
    洛谷 P2008 大朋友的数字
    [USACO10FEB]慢下来Slowing down
    HAOI2007 理想的正方形 单调队列
    滑动窗口
    双栈排序
    概率无向图模型与条件随机场的异同
    P-R曲线出现凹陷的原因
    MaskLab-实例分割(使用语义分割和方向特征精细化目标检测)
    模拟递归生成器
    递归生成器
  • 原文地址:https://www.cnblogs.com/wyxwyx/p/suffixautomaton.html
Copyright © 2020-2023  润新知