【BZOJ3998】弦论 [SAM]

弦论

Time Limit: 10 Sec  Memory Limit: 256 MB
[Submit][Status][Discuss]

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

Solution

  首先我们先构造一个后缀自动机,然后分类讨论:

  1. 如果T=0,点权为1。为什么呢?一个点有一个Right集合,一个Right集合可以表达多个子串 ,然后我们一个点 -> 另外一个点 其实不止一条边,我们每条边涵盖了一个信息,意味着 这个点->(走这条边)->到达了下一个点 通过这条边得到的那个新子串,而这多个新子串构成了一个 新的点。所以一条合法的路径,就表达了一个子串

  2. 如果T=1,点权为Right集合大小。Right集合是结束位置的合集,那么Right集合大小就表示这条路径表达的这个子串出现了多少次。

Code

  1 #include<iostream>  
  2 #include<string>  
  3 #include<algorithm>  
  4 #include<cstdio>  
  5 #include<cstring>  
  6 #include<cstdlib>  
  7 #include<cmath>
  8 using namespace std;
  9 typedef long long s64;
 10 
 11 const int ONE = 2e6+5;
 12 
 13 int n,T,k;
 14 char ch[500005];
 15 
 16 inline int get()
 17 {
 18         int res=1,Q=1;  char c;
 19         while( (c=getchar())<48 || c>57)
 20         if(c=='-')Q=-1;
 21         if(Q) res=c-48; 
 22         while((c=getchar())>=48 && c<=57) 
 23         res=res*10+c-48;
 24         return res*Q; 
 25 }
 26 
 27 struct SAM
 28 {
 29         int v[500005], q[ONE], num[ONE], size[ONE];
 30         int a[ONE][28], len[ONE], fa[ONE], New;
 31         int last, cnt;
 32         
 33         SAM() {last = cnt = 1;}
 34         void Add(int c)
 35         {
 36             int x=last, New=last=++cnt;
 37             len[New] = len[x] + 1;
 38             num[New] = 1;
 39             while(x && !a[x][c]) a[x][c] = New, x = fa[x];
 40             if(!x) {fa[New] = 1; return;}
 41             
 42             int q = a[x][c];
 43             if(len[x] + 1 == len[q]) fa[New] = q;
 44             else
 45             {
 46                 int Nq = ++cnt; len[Nq] = len[x] + 1;
 47                 memcpy(a[Nq], a[q], sizeof(a[q]));
 48                 fa[Nq] = fa[q];
 49                 fa[New] = fa[q] = Nq;
 50                 while(a[x][c] == q) a[x][c] = Nq, x = fa[x];
 51             }
 52         }
 53         
 54         void Update()
 55         {
 56             for(int i=1;i<=cnt;i++) v[len[i]]++;
 57             for(int i=1;i<=n;i++) v[i] += v[i-1];
 58             for(int i=cnt;i>=1;i--) q[v[len[i]]--] = i;
 59             
 60             
 61             for(int i=cnt; i>=1; i--)
 62             {
 63                 int x = q[i];
 64                 if(!T) num[x] = 1; else num[fa[x]] += num[x];    
 65             }
 66             num[1] = 0;
 67             
 68             for(int i=cnt; i>=1; i--)
 69             {
 70                 int x = q[i];
 71                 size[x] = num[x];
 72                 for(int j=1; j<=26; j++)
 73                     size[x] += size[a[x][j]];
 74             }
 75         }
 76         
 77         void Dfs(int u,int k)
 78         {
 79             if(k <= num[u]) return;
 80             k -= num[u];
 81             for(int j=1; j<=26; j++)
 82             if(a[u][j])
 83             {
 84                 if(k > size[a[u][j]]) k -= size[a[u][j]];
 85                 else
 86                 {
 87                     printf("%c",j+'a'-1);
 88                     Dfs(a[u][j], k);
 89                     return;
 90                 }
 91             }
 92         }
 93 }S;
 94 
 95 int main()
 96 {
 97         scanf("%s",ch+1); n = strlen(ch+1);
 98         T = get();    k = get();
 99         for(int i=1;i<=n;i++) S.Add(ch[i]-'a'+1);
100         
101         S.Update();
102         
103         if(k > S.size[1]) printf("-1");
104         else S.Dfs(1, k);
105     
106 }
View Code

 

转载于:https://www.cnblogs.com/BearChild/p/6841659.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值