bzoj 3998: [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


建出后缀自动机,然后在后缀自动机上跑一下就可以。

维护后面有几种情况

切记虚点处置为0。。

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
int T;
int lx;
struct sam
{
	 int son[1000005][26],pre[1000005],step[1000005];
	 int v[1000005],q[1000005];
	 int s[1000005],sum[1000005];
	 int last,tot;
	 sam()
	 {
	      last=1;
	      tot=1;
	 }
	 inline void push_back(int v)
	 {
	      tot++;
	      step[tot]=v;
	 }
     inline void extend(int x)
     {
          push_back(step[last]+1);
          int p=last,np=tot;s[np]=1;
          while(son[p][x]==0&&p!=0)
          {
               son[p][x]=np;
               p=pre[p];
          }
          if(p==0)
               pre[np]=1;
          else
          {
               int q=son[p][x];
               if(step[q]!=step[p]+1)
               {
                    push_back(step[p]+1);
                    //tot++;
                    int nq=tot;
                    int i;
                    for(i=0;i<=25;i++)
                         son[nq][i]=son[q][i];
                    pre[nq]=pre[q];
                    pre[q]=pre[np]=nq;
                    while(son[p][x]==q)
                    {
                         son[p][x]=nq;
                         p=pre[p];
                    }
               }
               else
                    pre[np]=q;
          }
          last=np;
     }
     inline void build()
     {
     	  string x;
          cin>>x;
          lx=x.size();
          int i;
          for(i=0;i<=lx-1;i++)
               extend(x[i]-'a');
     }
     inline void prep()
     {
     	  int i,j;
          for(i=1;i<=tot;i++)
               v[step[i]]++;
          for(i=1;i<=tot;i++)
               v[i]+=v[i-1];
          for(i=tot;i>=1;i--)
               q[v[step[i]]--]=i;
         // for(i=1;i<=tot;i++)
              // s[i]=1;
          for(i=tot;i>=1;i--)
          {
               if(T==1)
                    s[pre[q[i]]]+=s[q[i]];
               else
                    s[q[i]]=1;
          }
          s[1]=0;
          for(i=tot;i>=1;i--)
          {
               sum[q[i]]+=s[q[i]];
               for(j=0;j<=25;j++)
                    sum[q[i]]+=sum[son[q[i]][j]];
          }
     }
     inline void dfs(int d,int k)
     {
          if(k<=s[d])
               return ;
          k-=s[d];
          int i;
          for(i=0;i<=25;i++)
          {
          	   int t=son[d][i];
               if(t!=0)
               {
                    if(k<=sum[t])
                    {
                         printf("%c",i+'a');
                         dfs(t,k);
                         return ;
                    }
                    k-=sum[t];
               }
          }
     }
}sam;
int main()
{
     //freopen("data.in","r",stdin);
     //freopen("data.out","w",stdout);
     sam.build();
     int k;
	 scanf("%d%d",&T,&k);
	 sam.prep();
	 if(k>sam.sum[1])
	      printf("-1\n");
	 else
	 {
	      sam.dfs(1,k);
	      printf("\n");
	 }
     return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值