[BZOJ3998][TJOI2015]弦论-后缀树

弦论

Description

对于一个给定长度为N的字符串,求它的第K小子串是什么。

Input

第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。

Output

输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1

Sample Input

aabc
0 3

Sample Output

aab

HINT

N<=5*10^5
T<2
K<=10^9


出于某种目的寻找后缀树的题目,却一无所获……
然后就随手找了一道后缀自动机的题……
然后就很轻松地切掉了???


思路:
考虑到每一个子串均可以被表示为一个后缀的前缀,那么,建出后缀树。
然后,发现每一条边都有等于其代表的字符串长度的个数的子串。
那么,统计出每一条边的字符串个数 cnt[i] ,以及每一个点子树内及其父亲边的字符串个数之和 sum[i]
然后进行一次dfs,每走到一个节点,遍历其所有儿子。
如果当前儿子的 sum[i] 小于当前的 k ,则将当前k减去 sum[i] ,并继续遍历下一个儿子。
否则,判断当前儿子父亲边的 cnt[i] 是否大于等于当前的 k
如果是,那么答案子串的结尾显然就在当前边上,找到这个位置并输出。
否则,输出当前边所代表的字符串并递归该儿子。

T=1时,每条边的 cnt[i] 要乘以其子树内的叶子结点个数,因为每个当前边子树内的后缀均对当前边有1的贡献,而这样的后缀数量正是叶子节点的数量。 T=0 时则不用。

那么这样就做完了~
如果觉得这份代码很眼熟,那么你上一次看到的代码可能跟这次的代码作者相同

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

typedef long long ll;
const int N=1000009;
const ll inf=1e18+7;

int n,t,k,siz[N];
ll ans[N],cnt[N],sum[N];
char str[N];

namespace suffix_tree
{
    const int M=27;
    const int Inf=1e9+7;

    char s[N];
    int root,pos,last;
    int actpos,actedge,actlen,remain;
    int l[N],r[N],fail[N],ch[N][M],tot;

    inline int len(int x){return min(r[x],pos+1)-l[x];}
    inline int newnode(int lbound,int rbound=Inf)
    {
        l[++tot]=lbound;r[tot]=rbound;
        return tot;
    }
    inline void link(int x)
    {
        if(last)fail[last]=x;
        last=x;
    }
    inline bool walk(int x)
    {
        if(actlen>=len(x))
        {
            actedge+=len(x);
            actlen-=len(x);
            actpos=x;
            return true;
        }
        return false;
    }
    inline void init()
    {
        last=tot=remain=actedge=actlen=0;
        pos=-1;root=actpos=newnode(-1,-1);
    }
    inline void extend(int c)
    {
        s[++pos]=c;
        last=0;
        remain++;

        while(remain>0)
        {
            if(actlen==0)
                actedge=pos;
            if(ch[actpos][s[actedge]]==0)
            {
                ch[actpos][s[actedge]]=newnode(pos);
                link(actpos);
            }
            else
            {
                int nxt=ch[actpos][s[actedge]];
                if(walk(nxt))continue;
                if(s[l[nxt]+actlen]==c)
                {
                    actlen++;
                    link(actpos);
                    break;
                }
                int split=newnode(l[nxt],l[nxt]+actlen);
                ch[actpos][s[actedge]]=split;
                ch[split][c]=newnode(pos);
                l[nxt]+=actlen;
                ch[split][s[l[nxt]]]=nxt;
                link(split);
            }
            remain--;

            if(actpos==root && actlen>0)
            {
                actlen--;
                actedge=pos-remain+1;
            }
            else
                actpos=(fail[actpos]?fail[actpos]:root);
        }
    }
}

inline void dfs(int u)
{
    using namespace suffix_tree;
    siz[u]=0;
    for(int i=0;i<M;i++)
        if(ch[u][i])
        {
            dfs(ch[u][i]);
            siz[u]+=siz[ch[u][i]];
            sum[u]+=sum[ch[u][i]];
        }
    if(!siz[u])
    {
        siz[u]=1;
        sum[u]=cnt[u]=len(u)-1;
    }
    else
        sum[u]+=(cnt[u]=(t==1?siz[u]:1)*len(u));
}

inline void putstr(int st,int ed)
{
    using namespace suffix_tree;
    for(int i=st;i<ed;i++)
        putchar(s[i]+'a');
}

inline void dfs2(int u,int res)
{
    using namespace suffix_tree;
    for(int i=0;i<M;i++)
        if(ch[u][i])
        {
            if(res<=sum[ch[u][i]])
            {
                if(res>cnt[ch[u][i]])
                {
                    putstr(l[ch[u][i]],r[ch[u][i]]);
                    dfs2(ch[u][i],res-cnt[ch[u][i]]);
                    return;
                }
                else
                {
                    int block=(t==1?siz[ch[u][i]]:1);
                    putstr(l[ch[u][i]],l[ch[u][i]]+(res+block-1)/block);
                    return;
                }
            }
            else
                res-=sum[ch[u][i]];
        }
}

int main()
{
    scanf("%s",str+1);
    scanf("%d%d",&t,&k);
    n=strlen(str+1);
    suffix_tree::init();
    for(int i=1;i<=n;i++)
        suffix_tree::extend(str[i]-'a');
    suffix_tree::extend(26);

    dfs(suffix_tree::root);
    if(k>sum[suffix_tree::root])
        puts("-1");
    else
        dfs2(suffix_tree::root,k);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值