bzoj3998: [TJOI2015]弦论

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 #define maxn 1000005
 7 #define maxm 500005
 8 using namespace std;
 9 
10 typedef long long ll;
11 ll temp[26],sum[maxn];
12 int n,t,k,tot,root,last,tmp[maxn],dist[maxn],fa[maxn],son[maxn][26],ri[maxn];
13 bool bo=0;
14 char st[maxm];
15 struct Tsegment{
16     void prepare(){tot=root=last=1;}
17     int newnode(int x){dist[++tot]=x; return tot;}
18     void add(int x){
19         int p=last,np=newnode(dist[last]+1); last=np;
20         for (;p&&!son[p][x];p=fa[p]) son[p][x]=np;
21         if (p==0) fa[np]=root;
22         else{
23             int q=son[p][x];
24             if (dist[p]+1==dist[q]) fa[np]=q;
25             else{
26                 int nq=newnode(dist[p]+1);
27                 memcpy(son[nq],son[q],sizeof(son[q]));
28                 fa[nq]=fa[q],fa[q]=fa[np]=nq;
29                 for (;p&&son[p][x]==q;p=fa[p]) son[p][x]=nq;
30             }
31         }
32     }
33     void dfs(int x,int rank){
34         if (rank<=0){
35             bo=1;
36             return;
37         }
38         memset(temp,0,sizeof(temp));
39         for (int i=0;i<26;i++){
40             if (i==0) temp[i]=sum[son[x][i]];
41             else temp[i]=temp[i-1]+sum[son[x][i]];
42         }
43         for (int i=0;i<26;i++){
44             if (!son[x][i]) continue;
45             if (temp[i]>=rank){
46                 printf("%c",i+'a');
47                 if (i==0){
48                     if (t==0) dfs(son[x][i],rank-1);
49                     else dfs(son[x][i],rank-ri[son[x][i]]);
50                     if (bo==1) return;
51                 }else{
52                     if (t==0) dfs(son[x][i],rank-1-temp[i-1]);
53                     else dfs(son[x][i],rank-ri[son[x][i]]-temp[i-1]);
54                     if (bo==1) return;
55                 }
56             }
57         }
58     }
59 }SAM;
60 
61 int main(){
62     scanf("%s",st),n=strlen(st);
63     SAM.prepare();
64     for (int i=0;i<n;i++) SAM.add(st[i]-'a');
65     memset(sum,0,sizeof(sum));
66     for (int i=1;i<=tot;i++) sum[dist[i]]++;
67     for (int i=1;i<=n;i++) sum[i]+=sum[i-1];
68     for (int i=1;i<=tot;i++) tmp[sum[dist[i]]--]=i;
69     scanf("%d%d",&t,&k);
70     last=root;
71     memset(ri,0,sizeof(ri));
72     for (int i=0;i<n;i++){
73         int x=st[i]-'a';
74         last=son[last][x],ri[last]=1;
75     }
76     for (int i=tot;i>=1;i--){
77         int x=tmp[i];
78         if (fa[x]) ri[fa[x]]+=ri[x];
79     }
80     ri[root]=0;
81     memset(sum,0,sizeof(sum));
82     for (int i=tot;i>=1;i--){
83         int x=tmp[i];
84         if (t==0) sum[x]=1;
85         else sum[x]=ri[x];
86         for (int j=0;j<26;j++){
87             if (son[x][j]) sum[x]+=sum[son[x][j]];
88         }
89     }
90     sum[root]=bo=sum[0]=0;
91     SAM.dfs(root,k);
92     if (bo==0) printf("-1
");
93     else printf("
");
94     return 0;
95 }
View Code

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3998

题目大意:对于一个给定长度为N的字符串,求它的第K小子串是什么。

 第一行是一个仅由小写英文字母构成的字符串S。

第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
 

 做法:看到这题,正解是对这个字符串建立一个后缀自动机,对每个节点记录一个信息:往下面匹配能匹配多少个字符串(在计算是,若t=0,则初始为1,否则初始权值为该节点的right值),最后从root开始贪心即可。

后缀自动机+贪心。

原文地址:https://www.cnblogs.com/OYzx/p/5551877.html