后缀自动机

发布时间 2023-09-07 17:10:37作者: Gzyoung

\(Sam\) 复杂度和空间都成线性,但不能只开 \(n\)

\(endpos\)

1,定义 \(endpos\) 为每个子串出现的开头集合

2,定义 \(Sam\) 每个节点为“状态”,则每个状态对应着一个或者多个 \(endpos\) 相同的集合

后缀链接\(link\)

1,连向当前子串后缀中非同一 \(endpos\) 的最大那个

code:洛谷P3804后缀自动机

#include<bits/stdc++.h>
using namespace std;
const int N = 3e6+17;
int i, j, n, m, k, top = 1, u = 1;
char s[N];
vector<int> v[N];
long long ans;
struct pt {
    int len, f, cnt, nx[26];
    #define f(p) d[p].f//后缀链接link 
    #define len(p) d[p].len//同一 endpos下的最大字符串长度 
    #define cnt(p) d[p].cnt//该字符串(或者endpos集合大小)出现次数 
    int& operator [] (const int&x) {return nx[x-'a'];}
} d[N];//源点是1 
void ins(int x) {
    int p = u, q, t, i; 
    u = ++top;//新建节点 
    len(u) = len(p)+1;//长度为上次原串被包含的节点,即终止链的末尾 
    cnt(u) = 1;//标记出现次数 
    for(; p&&!d[p][x]; p=f(p)) d[p][x] = u;//顺着终止链找到原来的所有后缀,然后连一条当前字符的边到u,这样方能加入新的后缀 
    if(p) {
        q = d[p][x];
        if(len(p)+1 == len(q)) f(u) = q;//q里面没有比新后缀更长的串 
        else {//分裂q,一部分包含比新后缀更长的串,一部分(t)包含新后缀本身及比新后缀短的串 
            d[t=++top] = d[q], len(t) = len(p)+1, cnt(t) = 0;
            for(; p&&d[p][x]==q; p=f(p)) d[p][x] = t;//把新后缀及新后缀的后缀从q中剔除 
            f(u) = f(q) = t;
        }
    } else f(u) = 1;//如果p的link都可以直接连向x,那么u直接连向空集 
}
void dfs(int x) {
    for(int y: v[x]) {
        dfs(y);
        cnt(x) += cnt(y);
    }
    if(cnt(x) > 1)
    ans = max(ans, 1ll*cnt(x)*len(x));
}
signed main() {
    scanf("%s", s+1); 
    for(i=1; s[i]; i++) ins(s[i]);
    for(i=2; i<=top; i++) v[f(i)].push_back(i);
    dfs(1), printf("%lld", ans);
    return 0;
}