zoukankan      html  css  js  c++  java
  • [bzoj3998][TJOI2015]弦论

    后缀自动机丝薄题。

    求给定字符串$s$的第$k$大的子串。分unique之后的和不unique的两种询问。


    首先构建出SAM。

    相同子串算一个的情况:

    SAM上所有路径组成字符串$s$的全部子串,每个状态向下不管怎么走,形成的串都是以当前状态为前缀的。(废话)

    所以我们只要知道以当前串为前缀的串有多少,不就可以像在平衡树上搜索一样找到第$k$大吗。

    即从当前点开始,向下有多少路径。拓扑跑一下就行了。$sum[u]=sum sum[v] +1$。

    另一种情况:

    思路和第一种完全一样,区别在于所在位置不同的相同子串本质不同了。我们发现$right$集合的大小不就是当前状态的有多少个吗。所以我们先求出每个点$u$的$right$集合有多大($u$的$right$集合就是所有以$u$为$parent$(也有叫$link$,我叫$fa$)的点的$right$集合的并集呀),一个道理,拓扑跑一下。$sum[u]=sum sum[v]+|right(u)|$

    代码可以简化的地方很多。。

    看大佬们的代码都不用dfs去统计,蒟蒻想了半天发现自己傻掉了。因为我们知道不管在SAM的DAG中,还是在$parent$树中,儿子的$len$($max$?就是丽洁神犇ppt里的$max$)都比父亲大(DAG上说父子的话不太严密,但意思就是那样)。于是按$len$排序(注意可以基数排序!)然后再转移就一定不会错啦。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000010;
    typedef long long ll;
    int len[N],fa[N],ch[N][26];
    int las,sz;
    int opt,n,val[N],sum[N],v[N],q[N];
    void ins(int c){
        int now=++sz;len[now]=len[las]+1;
        int p,q;val[now]=1;
        for(p=las;~p&&!ch[p][c];p=fa[p])
        ch[p][c]=now;
        if(!~p)fa[now]=0;
        else{
            q=ch[p][c];
            if(len[q]==len[p]+1)
                fa[now]=q;
            else{
                int r=++sz;
                fa[r]=fa[q],len[r]=len[p]+1;
                for(int i=0;i<26;i++)
                ch[r][i]=ch[q][i];
                for(;~p&&ch[p][c]==q;p=fa[p])
                ch[p][c]=r;
                fa[now]=fa[q]=r;
            }
        }
        las=now;
    }
    void init(){
        for(int i=0;i<=sz;i++)v[len[i]]++;
        for(int i=1;i<=n;i++)v[i]+=v[i-1];
        for(int i=sz;~i;i--)
        q[v[len[i]]--]=i;
        for(int i=sz+1;i;i--){
            int t=q[i];
            if(opt==1)val[fa[t]]+=val[t];
            else val[t]=1;
        }
        val[0]=0;
        for(int i=sz+1;i;i--){
            int t=q[i];sum[t]=val[t];
            for(int j=0;j<26;j++)
            sum[t]+=sum[ch[t][j]];
        }
    }
    void dfs(int u,int rk){
        if(rk<=val[u])return;
        rk-=val[u];
        for(int i=0;i<26;i++)
        if(ch[u][i]){
            int t=ch[u][i];
            if(rk<=sum[t]){
                putchar(i+'a');
                dfs(t,rk);
                return;
            }
            rk-=sum[t];
        }
    }
    void solve(){
        init();
        int k;scanf("%d",&k);
        dfs(0,k);
    }
    char s[N];
    int main(){
        scanf("%s",s);fa[0]=-1;
        n=strlen(s);
        for(int i=0;i<n;i++)
        ins(s[i]-'a');
        scanf("%d",&opt);
        solve();
    }

     

  • 相关阅读:
    C语言寒假大作战02
    C语言寒假大作战01
    学习总结
    C语言I作业11
    C语言I作业10
    C语言I博客作业09
    C语言I作业08
    实验五、单元测试
    实验四 代码审查
    UML 建模工具的安装与使用
  • 原文地址:https://www.cnblogs.com/orzzz/p/8306726.html
Copyright © 2011-2022 走看看