P3804 【模板】后缀自动机

题目链接:

https://www.luogu.org/problem/P3804

题意:

给出一个字符串,求出所有出现次数不为1的子串,长度×出现次数的最大值

数据范围:

$1leq |S| leq 1000 000$

分析: 

用SAM求出所有子串出现的次数即可

洛谷模板:

#include<bits/stdc++.h>
using namespace std;const int N=2*1e6+10;typedef long long ll;
char S[N];
struct suffixautomation
{
    int mp[N][30];int fa[N];int ed;int ct;int len[N];int siz[N];int deg[N];
    suffixautomation(){ed=ct=1;}
    inline void ins(int c,int pos)
    {
        int p=ed;siz[ed=++ct]=1;len[ed]=pos;//先初始化size和len
        for(;p&&mp[p][c]==0;p=fa[p]){mp[p][c]=ed;}//然后顺着parent树的路径向上找
        if(p==0){fa[ed]=1;return;}int q=mp[p][c];//case1
        if(len[p]+1==len[q]){fa[ed]=q;return;}//case2
        len[++ct]=len[p]+1;//case 3
        for(int i=1;i<=26;i++){mp[ct][i]=mp[q][i];}
        fa[ct]=fa[q];fa[q]=ct;fa[ed]=ct;
        for(int i=p;mp[i][c]==q;i=fa[i]){mp[i][c]=ct;}
    }
    void solve(){
        queue<int>que;
        for(int i=2;i<=ct;i++)deg[fa[i]]++;
        for(int i=1;i<=ct;i++)if(deg[i]==0)que.push(i);
        while(que.size()){
            int x=que.front();
            que.pop();
            if(x==1)break;
            int y=fa[x];
            siz[y]+=siz[x];
            deg[y]--;
            if(deg[y]==0)que.push(y);
        }
        ll ans=0;
        for(int i=1;i<=ct;i++)if(siz[i]>1)ans=max(ans,(ll)len[i]*siz[i]);
        printf("%lld
",ans);
    }
}sam;
int main()
{
    scanf("%s",S+1);//没啥好说的,建sam然后dfs
    int len=strlen(S+1);
    for(int i=1;i<=len;i++)sam.ins(S[i]-'a'+1,i);
    sam.solve();
    return 0;//拜拜程序~
}

  

  

hiho模板:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXL = 1000000;
string s;
int n = 0,len,st;
int maxlen[2 * MAXL + 10], minlen[2 * MAXL + 10], trans[2 * MAXL + 10][26], slink[2 * MAXL + 10];
int endps[2 * MAXL +10],deg[2* MAXL+10];
int new_state(int _maxlen, int _minlen, int* _trans, int _slink) {
        maxlen[n] = _maxlen;
        minlen[n] = _minlen;
        for(int i = 0; i < 26; i++) {
                if(_trans == NULL)
                        trans[n][i] = -1;
                else
                        trans[n][i] = _trans[i];
        }
        slink[n] = _slink;
        return n++;
}

int add_char(char ch, int u) {
        int c = ch - 'a';
        int z = new_state(maxlen[u] + 1, -1, NULL, -1);
        int v = u;
        while(v != -1 && trans[v][c] == -1) {
                trans[v][c] = z;
                v = slink[v];
        }
        if(v == -1) { //最简单的情况,suffix-path(u->S)上都没有对应字符ch的转移
                minlen[z] = 1;
                slink[z] = 0;
                return z;
        }
        int x = trans[v][c];
        if(maxlen[v] + 1 == maxlen[x]) { //较简单的情况,不用拆分x
                minlen[z] = maxlen[x] + 1;
                slink[z] = x;
                return z;
        }
        int y = new_state(maxlen[v] + 1, -1, trans[x], slink[x]); //最复杂的情况,拆分x
        slink[y] = slink[x];
        minlen[x] = maxlen[y] + 1;
        slink[x] = y;
        minlen[z] = maxlen[y] + 1;
        slink[z] = y;
        int w = v;
        while(w != -1 && trans[w][c] == x) {
                trans[w][c] = y;
                w = slink[w];
        }
        minlen[y] = maxlen[slink[y]] + 1;
        return z;
}
int main(){
    int u=new_state(0,0,NULL,-1);
    cin>>s;
    len=s.size();
    for(int i=0;i<len;i++){
        u=add_char(s[i],u);
        endps[u]=1;
    }
    ll ans=0;
    for(int i=1;i<n;i++){
        deg[slink[i]]++;
    }
    queue<int>que;
    for(int i=1;i<n;i++)
        if(deg[i]==0)que.push(i);
    while(que.size()){
        int x=que.front();
        que.pop();
        if(x==0)break;
        endps[slink[x]]+=endps[x];
        deg[slink[x]]--;
        if(deg[slink[x]]==0)que.push(slink[x]);
    }
    for(int i=1;i<n;i++)
        if(endps[i]!=1)ans=max(ans,(ll)endps[i]*maxlen[i]);
    printf("%lld
",ans);
    return 0;
}
  

  

原文地址:https://www.cnblogs.com/carcar/p/11631160.html