P3975 [TJOI2015]弦论

题目描述

为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?

输入输出格式

输入格式:

 

第一行是一个仅由小写英文字母构成的字符串s

第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。k的意义见题目描述。

 

输出格式:

 

输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。

 

输入输出样例

输入样例#1:
aabc
0 3
输出样例#1: 
aab
输入样例#2: 
aabc
1 3
输出样例#2: 
aa
输入样例#3:
aabc
1 11
输出样例#3: 
-1

说明

数据范围

对于10%的数据,n ≤ 1000。

对于50%的数据,t = 0。

对于100%的数据,n ≤ 5 × 10^5, t < 2, k ≤ 10^9。

题解:首先题目有两个要求,t=1,不同位置相同子串看做不同;t=0:,不同位置的相同子串看成相同;

先对这个串建个SAM,再对所有节点按照maxlen排下序。这样就可以从长到短地总结答案。

首先对每个节点维护一个当前节点所占的子串个数。假如t=1就按照Parent树里从叶子节点一层一层往上推的顺序,计算每个串出现的次数。t=0就不用管,直接设成1

这样就可以维护出每个节点往后推会有多少个串。。(直接加起来

然后在SAM上按照字典序往下匹配就可以了

 

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=1e6+10;
char s[maxn];
int k,t;
struct SAM{//求字典序第K小串 
    int l[maxn<<1],fa[maxn<<1],nxt[maxn<<1][26];
    int last,cnt,c[maxn<<1],siz[maxn<<1],sum[maxn<<1],a[maxn<<1];
    void Init()
    {
        memset(siz,0,sizeof(siz));
        memset(c,0,sizeof(c));
        memset(sum,0,sizeof(sum));
        memset(a,0,sizeof(a));
        last=cnt=1;
        memset(nxt[1],0,sizeof(nxt[1]));
        fa[1]=l[1]=0;
    }
    int NewNode()
    {
        cnt++;
        memset(nxt[cnt],0,sizeof(nxt[cnt]));
        fa[cnt]=l[cnt]=0;
        return cnt;
    }
    void Add(int ch)
    {
        int p=last,np=NewNode();
        last=np; l[np]=l[p]+1;
        siz[np]=1;
        while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p];
        if(!p) fa[np]=1;
        else
        {
            int q=nxt[p][ch];
            if(l[q]==l[p]+1) fa[np]=q;
            else
            {
                int nq=NewNode();
                memcpy(nxt[nq],nxt[q],sizeof(nxt[q]));
                fa[nq]=fa[q];
                l[nq]=l[p]+1;
                fa[np]=fa[q]=nq;
                while(nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p];
            }
        }
    }
    void Build()
    {
        int len=strlen(s+1);
        for(int i=1;i<=len;i++) Add(s[i]-'a');
    }
    void topusort()
    {
        for(int i=1;i<=cnt;++i) c[l[i]]++;
        for(int i=1;i<=cnt;++i) c[i]+=c[i-1];
        for(int i=1;i<=cnt;++i) a[c[l[i]]--]=i;
        for(int i=cnt;i;--i)
        {//t==1:不同位置的相同子串视为不同 
            if(t) siz[fa[a[i]]]+=siz[a[i]];
            else siz[a[i]]=1;//t==0:视为相同 
        }
        siz[1]=0;
        for(int i=cnt;i;--i)
        {
            sum[a[i]]=siz[a[i]];
            for(int j=0;j<26;++j)
                if(nxt[a[i]][j]) sum[a[i]]+=sum[nxt[a[i]][j]];
        }
    }    
    void dfs()
    {
        if(k>sum[1]){puts("-1");return ;}
        int now=1;
        while(k>0)
        {
            int p=0;
            while(k>sum[nxt[now][p]])
            {
                k-=sum[nxt[now][p]];
                p++;
            }
            now=nxt[now][p];
            putchar('a'+p);
            k-=siz[now];
        }
        return ;
    }
} sam;
int main()
{
    scanf("%s%d%d",s+1,&t,&k);
    sam.Init();
    sam.Build(); 
    sam.topusort();
    sam.dfs();
    return 0;
}
View Code

 

posted @ 2019-05-03 14:45  StarHai  阅读(221)  评论(0编辑  收藏  举报