3998: [TJOI2015]弦论
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
<script type="math/tex" id="MathJax-Element-15"> </script>
解:
居然一遍写对后缀自动机哈哈哈。
很简单的做法。先算出每个节点算出现几次,如果t=0就是全部出现一次,否则就是把非复制节点赋值1拓扑序上推一波。
接着在自动机上跑一边dp(一开始以为是树形结构,没跑dp。。。),记录每个点向下跑可以得到多少串。
贪心一下即可。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
struct lxy{
int to[26],p,len;
long long num,f;
}a[1000005];
int cnt=1,las=1,t,len;
int tax[500005],tp[1000005];
long long k;
char s[500005];
void insert(int c,int w){
a[++cnt].len=w;a[cnt].num=1;
int i;for(i=las;a[i].to[c]==0&&i!=0;i=a[i].p) a[i].to[c]=cnt;
las=cnt;
if(i==0){
a[cnt].p=1;return;
}
int q=a[i].to[c],nq;
if(a[i].len+1==a[q].len){
a[cnt].p=q;return;
}
nq=++cnt;a[nq]=a[q];a[nq].len=a[i].len+1;a[nq].num=0;
for(int j=i;a[j].to[c]==q;j=a[j].p) a[j].to[c]=nq;
a[q].p=nq;a[las].p=nq;
}
void findtp(){
for(int i=1;i<=cnt;i++) tax[a[i].len]++;
for(int i=1;i<=len;i++) tax[i]+=tax[i-1];
for(int i=1;i<=cnt;i++) tp[tax[a[i].len]--]=i;
}
void dfs(int u){
a[u].f=a[u].num;
for(int i=0;i<=25;i++)
if(a[u].to[i]!=0){
if(a[a[u].to[i]].f==0) dfs(a[u].to[i]);
a[u].f+=a[a[u].to[i]].f;
}
}
void findans(int u){
if(k<=a[u].num) return;
k-=a[u].num;
for(int i=0;i<=25;i++)
if(a[u].to[i]!=0){
if(a[a[u].to[i]].f>=k){
printf("%c",i+'a');
findans(a[u].to[i]);
return;
}
else k-=a[a[u].to[i]].f;
}
}
int main()
{
scanf("%s",s+1);
len=strlen(s+1);
for(int i=1;i<=len;i++) insert(s[i]-'a',i);
scanf("%d%lld",&t,&k);
findtp();
for(int i=cnt;i>=1;i--) a[a[tp[i]].p].num+=a[tp[i]].num;
if(t==0)
for(int i=1;i<=cnt;i++) a[i].num=1;
a[1].num=0;
dfs(1);
if(k>a[1].f){
printf("-1");
return 0;
}
findans(1);
}