BZOJ3998:[TJOI2015]弦论(SAM)

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

对于$t=0$的情况,我们将$right$集合赋值为$1$,否则就赋成正常的$right$集合大小。

因为$SAM$是一个$DAG$,所以可以对$right$集合求一个后缀和$sum$,然后用类似于平衡树查找第$k$大的方式找一下就好了。具体可以看代码,还是很好懂的……

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define N (1000009)
 5 using namespace std;
 6 
 7 int n,t,k,flag;
 8 char s[N];
 9 
10 struct SAM
11 {
12     int p,q,np,nq,last,cnt;
13     int fa[N],son[N][26],step[N],right[N];
14     int d[N],sum[N];
15     int wt[N],od[N];
16     SAM(){last=cnt=1;}
17     
18     void Insert(int x)
19     {
20         p=last; np=last=++cnt; step[np]=step[p]+1; right[np]=1;
21         while (!son[p][x] && p) son[p][x]=np, p=fa[p];
22         if (!p) fa[np]=1;
23         else
24         {
25             q=son[p][x];
26             if (step[p]+1==step[q]) fa[np]=q;
27             else
28             {
29                 nq=++cnt; step[nq]=step[p]+1;
30                 memcpy(son[nq],son[q],sizeof(son[q]));
31                 fa[nq]=fa[q]; fa[q]=fa[np]=nq;
32                 while (son[p][x]==q) son[p][x]=nq, p=fa[p];
33             }
34         }
35     }
36     void Calc()
37     {
38         for (int i=1; i<=cnt; ++i) wt[step[i]]++;
39         for (int i=1; i<=n; ++i) wt[i]+=wt[i-1];
40         for (int i=cnt; i>=1; --i) od[wt[step[i]]--]=i;
41         for (int i=cnt; i>=1; --i)    
42             if (t) right[fa[od[i]]]+=right[od[i]];
43             else right[od[i]]=1;
44         right[1]=0;
45         for (int i=cnt; i>=1; --i)
46         {
47             sum[od[i]]=right[od[i]];
48             for (int j=0; j<26; ++j)
49                 sum[od[i]]+=sum[son[od[i]][j]];
50         }
51     }
52     void Query(int x,int k)
53     {
54         if (k<=right[x]) return;
55         k-=right[x];
56         for (int i=0; i<26; ++i)
57         {
58             if (!son[x][i]) continue;
59             if (k<=sum[son[x][i]])
60             {
61                 flag=1;
62                 printf("%c",'a'+i);
63                 Query(son[x][i],k);
64                 return;
65             }
66             k-=sum[son[x][i]];
67         }
68     }
69 }SAM;
70 
71 int main()
72 {
73     scanf("%s%d%d",s,&t,&k);
74     n=strlen(s);
75     for (int i=0; i<n; ++i)
76         SAM.Insert(s[i]-'a');
77     SAM.Calc(); SAM.Query(1,k);
78     if (!flag) puts("-1");
79 }

转载于:https://www.cnblogs.com/refun/p/10419569.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值