P3975 (后缀自动机sort)

题目链接:

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

题意:

求出所有字串的第k大子串

有两种,第一种对于出现在不同位置的相同子串算作一个子串

第二种,对于不同位置的子串算作是不相同子串

数据范围:

$1leq |S| leq5000 00$

分析: 

对于第一种计算,endpos算作1,对于第二种,endpos是状态出现的次数

对于当前状态前缀出现的次数为endpos+子状态的出现次数

sam的拓扑序可以根据maxlen来排,maxlen越大,拓扑序肯定越小,无论是slink,还是trans边,都是短的maxlen链接长的maxlen

用桶排序实现$O(n)$为它们排序

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long ll;
char S[N/2];
char ans[N/2];
int fla,k,top;
struct suffixautomation
{
    int mp[N][30];int fa[N];int ed;int ct;int len[N];int siz[N];
    ll weight[N];
    int z[N],A[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(){
        //sort
        for(int i=1;i<=ct;i++)z[len[i]]++;
        for(int i=1;i<=ct;i++)z[i]+=z[i-1];
        for(int i=1;i<=ct;i++)A[z[len[i]]--]=i;

        for(int i=ct;i>=1;i--){
            if(fla)siz[fa[A[i]]]+=siz[A[i]];
            else siz[fa[A[i]]]=1;
        }
        for(int i=ct;i>=1;i--){
         //   cout<<A[i]<<endl;
            int v=A[i];
            weight[v]=siz[v];
            for(int j=1;j<=26;j++)
                if(mp[v][j])weight[v]+=weight[mp[v][j]];
        }
        weight[1]-=siz[1];
        if(k>weight[1]){
            printf("-1
");
            return ;
        }
        int now=1;
        while(1){
            for(int i=1;i<=26;i++)
                if(k>weight[mp[now][i]]){
                    k-=weight[mp[now][i]];
                }else{
                    now=mp[now][i];
                    ans[++top]='a'+i-1;
                    break;
                }
            if(k<=siz[now])break;
            else k-=siz[now];
        }
        ans[++top]=0;
        printf("%s
",ans+1);
    }
}sam;
int main()
{
    scanf("%s",S+1);
    scanf("%d %d",&fla,&k);
    int len=strlen(S+1);
    for(int i=1;i<=len;i++)sam.ins(S[i]-'a'+1,i);
    sam.solve();
    return 0;
}

  

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