P3975 [TJOI2015]弦论

P3975 [TJOI2015]弦论

一定要记得初始化!

后缀链接连接的节点所表示的字符串是该节点表示字符串的后缀,先将所有新增节点的dp[i]都置为1(除了拆点),一个节点所表示字符串的出现次数是其子树所有dp值之和(下标不同,本质相同的两个串是不同串)

如果下标不同,本质相同的两个串属于一个串的话,那么所有的dp[i]为1即可。因为,后缀树的所有节点包括了所有的子串。然后再按照Next转移即可。

Next 表示的是转移,通过每个节点的转移,可以得到后缀树中的所有子串

link 指向的是子串,所以 p 指向的节点一定会让 link[p] 表示的字符串在除 link[p] 表示的位置再出现一次。

// Created by CAD
#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+10;
namespace sam{
    int len[maxn],link[maxn],Next[maxn][26];
    int sz,last;
    int dp[maxn];
    void init(){                //记得初始化
        sz=last=0;
        len[0]=0,link[0]=-1;
    }
    void insert(char c){        //插入字符
        int now=++sz;
        len[now]=len[last]+1;
        int p=last;
        dp[now]=1;
        while(~p&&!Next[p][c-'a']){
            Next[p][c-'a']=now;
            p=link[p];
        }
        if(p==-1) link[now]=0;
        else{
            int q=Next[p][c-'a'];
            if(len[p]+1==len[q]) link[now]=q;
            else{
                int clone=++sz;
                len[clone]=len[p]+1;
                memcpy(Next[clone],Next[q],sizeof(Next[q]));
                link[clone]=link[q];
                while(~p&&Next[p][c-'a']==q){
                    Next[p][c-'a']=clone;
                    p=link[p];
                }
                link[q]=link[now]=clone;
            }
        }
        last=now;
    }
    vector<int> g[maxn];
    int dfs(int x){                     //预处理出每一个节点表示串的出现次数
        for(int i:g[x])
            dp[x]+=dfs(i);
        return dp[x];
    }
    bool vis[maxn];
    int sum[maxn];
    int dfs2(int x){                    //预处理出每一个节点其子树的贡献和
        if(vis[x]) return sum[x];vis[x]=1;
        for(int i=0;i<26;++i){
            int o=Next[x][i];
            if(o)   sum[x]+=dfs2(o);
        }
        return sum[x];
    }
    void out(int x,int k){
        if(dp[x]>=k) return;            //扣除该节点的贡献
        k-=dp[x];
        for(int i=0;i<26;++i){
            int o=Next[x][i];
            if(!o) continue;
            else if(sum[o]<k) k-=sum[o];//如果子节点的贡献比当前k小,那么扣除子节点的贡献
            else{                       //如果大于,说明当前节点或者当前节点的子树包括了第k小
                printf("%c",'a'+i);
                out(o,k);
                return ;
            }
        }
    }
    void solve(int t,int k){
        for(int i=1;i<=sz;++i)
            g[link[i]].push_back(i);
        dfs(0);
        for(int i=0;i<=sz;++i)
            sum[i]=t?dp[i]:(dp[i]=1);
        dp[0]=sum[0]=0;
        if(dfs2(0)<k) return puts("-1"),void();
        out(0,k);
    }
}
char s[maxn];
int main() {
    scanf("%s",s);
    int t,k;scanf("%d%d",&t,&k);
    sam::init();
    int n=strlen(s);
    for(int i=0;i<n;++i) sam::insert(s[i]);
    sam::solve(t,k);
    return 0;
}
原文地址:https://www.cnblogs.com/CADCADCAD/p/13522292.html