[TJOI2015]弦論(字尾自動機)
阿新 • • 發佈:2018-11-07
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
解:
居然一遍寫對字尾自動機哈哈哈。
很簡單的做法。先算出每個節點算出現幾次,如果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);
}