后缀自动机板子题

题意:给了一个字符串,然后里面有些子串出现次数大于1,求这种子串的出现次数乘以子串长度的最大值。

思路:

  1. 正常建好后缀自动机,将所有的np节点出现次数赋值为1
  2. 记录所有的父节点有几个儿子(不是后缀自动机上的儿子数,而是parent tree上的儿子数)
  3. 利用topo排序的思路更新所有节点的出现次数
  4. 然后就是更新答案
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "bits/stdc++.h"
#define pb push_back
#define ls l,m,now<<1
#define rs m+1,r,now<<1|1
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 1e6+10;
const int mod = 1e9+7;
const double eps = 1e-9;

char s[maxn];
int ch[maxn*2][26], len[maxn*2], fa[maxn*2];
int cnt[maxn*2], son[maxn*2];
int last=1, sz=1, n;

bool cmp(int a, int b) {
    return len[a]>len[b];
}

void add(int c) {
    int p=last, np=last=++sz;
    len[np]=len[p]+1; cnt[np]=1;
    for(; p&&!ch[p][c]; p=fa[p]) ch[p][c]=np;
    if(!p) fa[np]=1;
    else {
        int q=ch[p][c];
        if(len[q]==len[p]+1) fa[np]=q;
        else {
            int nq=++sz;
            fa[nq]=fa[q], len[nq]=len[p]+1;
            memcpy(ch[nq],ch[q],104);
            fa[q]=fa[np]=nq;
            for(; p&&ch[p][c]==q; p=fa[p]) ch[p][c]=nq;
        }
    }
}

int main() {
    //ios::sync_with_stdio(false);
    scanf("%s", s); n=strlen(s);
    for(int i=0; i<n; ++i) add(s[i]-'a');
    queue<int> q;
    for(int i=1; i<=sz; ++i) son[fa[i]]++;
    for(int i=1; i<=sz; ++i) if(!son[i]) q.push(i);
    while(q.size()) {
        int now=q.front(); q.pop();
        cnt[fa[now]]+=cnt[now];
        son[fa[now]]--;
        if(!son[fa[now]]) q.push(fa[now]);
    }
    ll ans=0;
    for(int i=1; i<=sz; ++i) if(cnt[i]>1) ans=max(ans,1LL*cnt[i]*len[i]);
    cout<<ans<<endl;
}