【BZOJ 3998】 3998: [TJOI2015]弦论 (SAM )

3998: [TJOI2015]弦论

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 2627  Solved: 881

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

Source

 

 

【分析】

  建SAM,然后跑。

  right数组要按照拓扑序来求啊!!!!

  然后累计儿子的和的时候也要用拓扑序。

  具体拓扑序:

  

for(int i=1;i<=tot;i++) v[t[i].step]++;
for(int i=1;i<=tot;i++) v[i]+=v[i-1];
for(int i=tot;i>=1;i--) q[v[t[i].step]--]=i;

  类似后缀数组那里的了。

  T=0,就right一开始都为1;T=1,就用right数组。

  空串算一个串,一开始k++。

  当然后缀数组也是可以的。

 

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define Maxn 500010
  8 
  9 struct node
 10 {
 11     int pre,last,son[30],step;
 12 }t[Maxn*2];
 13 int rt[Maxn*2],sm0[2*Maxn],sm1[2*Maxn];
 14 int v[2*Maxn],q[2*Maxn];
 15 
 16 struct sam
 17 {
 18     int last,tot;
 19     void extend(int k)
 20     {
 21         int np=++tot,p=last;
 22         t[np].step=t[last].step+1;
 23         rt[np]=1;
 24         while(p&&!t[p].son[k])
 25         {
 26             t[p].son[k]=np;
 27             p=t[p].pre;
 28         }
 29         if(!p) t[np].pre=1;
 30         else
 31         {
 32             int q=t[p].son[k];
 33             if(t[q].step==t[p].step+1) t[np].pre=q;
 34             else
 35             {
 36                 int nq=++tot;
 37                 memcpy(t[nq].son,t[q].son,sizeof(t[nq].son));
 38                 t[nq].step=t[p].step+1;
 39                 t[nq].pre=t[q].pre;
 40                 t[q].pre=t[np].pre=nq;
 41                 while(p&&t[p].son[k]==q)
 42                 {
 43                     t[p].son[k]=nq;
 44                     p=t[p].pre;
 45                 }
 46             }
 47         }
 48         last=np;
 49     }
 50     void init()
 51     {
 52         for(int i=1;i<=tot;i++) v[t[i].step]++;
 53         for(int i=1;i<=tot;i++) v[i]+=v[i-1];
 54         for(int i=tot;i>=1;i--) q[v[t[i].step]--]=i;
 55         
 56         // for(int i=1;i<=tot;i++) rt[i]=1;
 57         // for(int i=tot;i>=1;i--) rt[t[i].pre]+=rt[i];
 58         // rt[1]=1;
 59         for(int i=tot;i>=1;i--)
 60         {
 61             int nw=q[i];
 62             rt[t[nw].pre]+=rt[nw];
 63         }rt[1]=1;
 64         
 65         for(int i=tot;i>=1;i--)
 66         {
 67             int nw=q[i];
 68             sm0[nw]=1;sm1[nw]=rt[nw];
 69             for(int j=1;j<=26;j++) if(t[nw].son[j])
 70             {
 71                 sm0[nw]+=sm0[t[nw].son[j]];
 72                 sm1[nw]+=sm1[t[nw].son[j]];
 73             }
 74         }
 75     }
 76     void ffind(int opt,int k)
 77     {
 78         int sm,nw=1;
 79         k++;
 80         while(1)
 81         {
 82             sm=opt?rt[nw]:1;
 83             for(int i=1;i<=26;i++) if(t[nw].son[i])
 84             {
 85                 int ss=sm;
 86                 if(!opt) sm+=sm0[t[nw].son[i]];
 87                 else sm+=sm1[t[nw].son[i]];
 88                 if(sm>=k) {k-=ss;printf("%c",'a'+i-1);nw=t[nw].son[i];break;}
 89             }
 90             if(!opt&&k==1) break;
 91             if(opt&&k<=rt[nw]) break;
 92         }
 93         printf("\n");
 94     }
 95 }sam;
 96 
 97 char s[Maxn];
 98 
 99 int main()
100 {
101     scanf("%s",s);
102     int l=strlen(s);
103     sam.last=sam.tot=1;
104     for(int i=0;i<l;i++) sam.extend(s[i]-'a'+1);
105     sam.init();
106     int opt,k;
107     scanf("%d%d",&opt,&k);
108     if(!opt&&k+1>sm0[1]) printf("-1\n");
109     else if(opt&&k+rt[1]>sm1[1]) printf("-1\n");
110     else sam.ffind(opt,k);
111     return 0;
112 }
View Code

 

2017-04-17 13:59:36

  

posted @ 2017-04-17 13:59  konjak魔芋  阅读(267)  评论(0编辑  收藏  举报