BZOJ 3998 [TJOI2015]弦论 (后缀自动机)

题目大意:

给你一个字符串,求字典序第$k$小的串是什么

先建出$sam$和$parent$树

在$trs$图中,从根走向节点x的每一条路径都是x能代表的一个字符串

$parent$树以 某个节点为根的子树内$endpos$节点的数量,表示这个节点 能代表的所有字符串 正序作为后缀在所有前缀串中出现的次数

利用这个性质,我们在$trs$图中反向拓扑,就能累加出以某个节点x代表的某个字符串为前缀的子串总数量

因为从一个节点$x$沿一条正向边$trs[x][i]$走向一个节点$y$,我们能表示的字符串就是在$x$原来表示的某个字符串$S$之后添加一个字符$i$,是正序的

而反向拓扑是统计$S$后面能接的串$ixxx$的总数量

最后在$trs$图上26分即可

  1 #include <vector>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 505000
  6 #define S1 (N1<<1)
  7 #define ll long long
  8 #define uint unsigned int
  9 #define rint register int 
 10 #define il inline 
 11 #define inf 0x3f3f3f3f
 12 #define idx(X) (X-'a')
 13 using namespace std;
 14 
 15 int gint()
 16 {
 17     int ret=0,fh=1;char c=getchar();
 18     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 19     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
 20     return ret*fh;
 21 }
 22 
 23 int T,K,n,m,len,cte;
 24 char str[N1];
 25 
 26 namespace SAM{
 27 int trs[S1][26],pre[S1],dep[S1],ed[S1],la,tot;
 28 ll sz[S1],sum[S1];
 29 void init(){tot=la=1;}
 30 void Build_SAM(int x)
 31 {
 32     int p,q,np,nq;
 33     np=++tot,p=la,la=np;
 34     dep[np]=dep[p]+1,ed[np]=1;
 35     for(;p&&!trs[p][x];p=pre[p]) trs[p][x]=np;
 36     if(!p) pre[np]=1;
 37     else{
 38         q=trs[p][x];
 39         if(dep[q]==dep[p]+1) pre[np]=q;
 40         else{
 41             pre[nq=++tot]=pre[q];
 42             pre[np]=pre[q]=nq;
 43             dep[nq]=dep[p]+1;
 44             memcpy(trs[nq],trs[q],sizeof(trs[q]));
 45             for(;p&&trs[p][x]==q;p=pre[p]) trs[p][x]=nq; 
 46         }
 47     }
 48 }
 49 bool cmp(int x,int y){return x<y;}
 50 
 51 int ret[N1],tp;
 52 int hs[S1],que[S1];
 53 void solve()
 54 {
 55     init();
 56     for(int i=1;i<=len;i++)
 57         Build_SAM(idx(str[i]));
 58     for(int i=2;i<=tot;i++)
 59         hs[dep[i]]++;
 60     for(int i=1;i<=len;i++)
 61         hs[i]+=hs[i-1];
 62     for(int i=1;i<=tot;i++)
 63         que[hs[dep[i]]--]=i;
 64     int x;
 65     for(int i=tot-1;i>=1;i--)
 66     {
 67         x=que[i];
 68         sz[x]+=ed[x]?1:0;
 69         if(!T) sz[x]=1;
 70         else sz[pre[x]]+=sz[x];
 71     }
 72     sz[1]=0;
 73     for(int i=tot-1;i>=0;i--)
 74     {
 75         x=que[i];
 76         sum[x]+=sz[x];
 77         for(int j=0;j<26;j++)
 78             sum[x]+=sum[trs[x][j]];
 79     }
 80     x=1;
 81     if(K>sum[1]){printf("-1\n");return;}
 82     while(1)
 83     {
 84         if(K<=sz[x]) {break;}
 85         K-=sz[x];
 86         for(int i=0;i<26;i++)
 87         {
 88             if(K>sum[trs[x][i]]){
 89                 K-=sum[trs[x][i]];
 90             }else{
 91                 ret[++tp]=i;
 92                 x=trs[x][i];
 93                 break;
 94             }
 95         }
 96     }
 97     for(int i=1;i<=tp;i++)
 98         printf("%c",ret[i]+'a');
 99     puts("");
100 }
101 };
102 
103 int main()
104 {
105     //freopen("t2.in","r",stdin);
106     scanf("%s",str+1);
107     len=strlen(str+1);
108     scanf("%d%d",&T,&K);
109     SAM::solve();
110     return 0;
111 }

 

posted @ 2018-12-10 15:51  guapisolo  阅读(172)  评论(0编辑  收藏  举报