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


思路

看这道题是处理K小字串,果断上后缀自动机啊
然后考虑怎么统计答案
如果T是0,那么可以每一个节点的大小是1,否则大小是right集合大小
然后toposort之后反向DP出经过这条转移边可以有多少种字串
然后就可以直接贪心了


toposort的数组也要开串长两倍啊啊啊


#include<bits/stdc++.h>
using namespace std;
#define fu(a,b,c) for(int a=b;a<=c;++a)
#define fd(a,b,c) for(int a=b;a>=c;--a)
const int CHARSET_SIZE=26;
#define N 500010
struct Node{
  int ch[CHARSET_SIZE],prt;
  int maxl,right;
  Node(int maxl=0,int right=0):ch(),prt(0),maxl(maxl),right(right){}
}t[N<<1];
int root,last,cur;
int topo[N<<1],buc[N];
int newnode(int maxl=0,int right=0){t[++cur]=Node(maxl,right);return cur;}
void init(){cur=0;root=last=newnode();}
void extend(int c){
  int u=newnode(t[last].maxl+1,1),v=last;
  for(;v&&!t[v].ch[c];v=t[v].prt)t[v].ch[c]=u;
  if(!v){t[u].prt=root;}
  else if(t[t[v].ch[c]].maxl==t[v].maxl+1){
    t[u].prt=t[v].ch[c];
  }else{
    int n=newnode(t[v].maxl+1,0),o=t[v].ch[c];
    memcpy(t[n].ch,t[o].ch,sizeof(t[o].ch));
    t[n].prt=t[o].prt;
    t[o].prt=t[u].prt=n;
    for(;v&&t[v].ch[c]==o;v=t[v].prt)t[v].ch[c]=n;
  }
  last=u;
}
void toposort(){
  int maxv=0;
  fu(i,1,cur){
    ++buc[t[i].maxl];
    maxv=max(maxv,t[i].maxl);
  }
  fu(i,1,maxv)buc[i]+=buc[i-1];
  fu(i,1,cur)topo[buc[t[i].maxl]--]=i;
  fu(i,1,maxv)buc[i]=0;
}
void cal_right(){
  toposort();
  fd(i,cur,1){
    int p=topo[i];
    t[t[p].prt].right+=t[p].right;
  }
}
char c[N],ans[N];
int T,K,dp[N<<1];
int main(){
  scanf("%s",c+1);
  scanf("%d%d",&T,&K);
  int len=strlen(c+1);
  init();
  fu(i,1,len)extend(c[i]-'a');
  cal_right();
  fu(i,2,cur)dp[i]=T?t[i].right:1;
  fd(i,cur,1)
    fu(j,0,25)
      dp[topo[i]]+=dp[t[topo[i]].ch[j]];
  if(K>dp[1]){printf("-1");return 0;}
  int now=1,siz=0;
  while(1){
    int tmp=T?t[now].right:1;
    if(now==1)tmp=0;
    if(K<=tmp)break;
    K-=tmp;
    fu(i,0,25)if(t[now].ch[i]){
      int v=t[now].ch[i];
      if(dp[v]>=K){ans[++siz]=i+'a';now=v;break;}
      else K-=dp[v];
    }
  }
  fu(i,1,siz)printf("%c",ans[i]);
  return 0;
}

转载于:https://www.cnblogs.com/dream-maker-yk/p/9696041.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值