• 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;
    }
    
  • 相关阅读:
    李彦宏:创业成功五招即可
    JS无聊之作——换肤切换样式
    从3个科技公司里学到的57条经验(转载)
    早该知道的7个JavaScript技巧
    ASP.NET Cookie 概述
    曝光SEO高手藏在内心的SEO秘籍
    18种最实用的网站推广方法大全
    javascript的IE和Firefox兼容性问题
    增加反向链接的35个技巧
    常用JS片段
  • 原文地址:https://www.cnblogs.com/CADCADCAD/p/13522292.html
Copyright © 2020-2023  润新知