1. 程式人生 > >bzoj3998 [TJOI2015]弦論

bzoj3998 [TJOI2015]弦論

parent freopen eof strlen utc -i name 我們 stdin

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

正解:後綴自動機。

我們可以使用後綴自動機來求出每一個後綴,那麽可以知道,一個字符串的出現次數就是它的$right$集合大小,也就是它在後綴自動機的$parent$樹上的子樹大小。

那麽如果$T=1$,就把一個點的$sz$統計成它的$parent$子樹,否則它的$sz$就是$1$,然後我們貪心地找出第$k$小子串就行了。

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 #define N (1000010)
 6 
 7 using namespace std;
 8 
 9 int ch[N][26],l[N],fa[N],sz[N],sum[N],c[N],id[N],n,t,k,la,tot,len;
10 char s[N]; 11 12 il void add(RG int c){ 13 RG int p=la,np=++tot; la=np,l[np]=l[p]+1,sz[np]=1; 14 for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np; 15 if (!p){ fa[np]=1; return; } RG int q=ch[p][c]; 16 if (l[q]==l[p]+1) fa[np]=q; else{ 17 RG int nq=++tot; l[nq]=l[p]+1; 18 fa[nq]=fa[q],fa[q]=fa[np]=nq;
19 memcpy(ch[nq],ch[q],sizeof(ch[q])); 20 for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 21 } 22 return; 23 } 24 25 il void dfs(RG int x,RG int k){ 26 if (k<=sz[x]) return; RG int c=sz[x]; 27 for (RG int i=0;i<26;++i) 28 if (k<=c+sum[ch[x][i]]){ 29 putchar(a+i); 30 return dfs(ch[x][i],k-c); 31 } else c+=sum[ch[x][i]]; 32 return; 33 } 34 35 int main(){ 36 #ifndef ONLINE_JUDGE 37 freopen("kth.in","r",stdin); 38 freopen("kth.out","w",stdout); 39 #endif 40 scanf("%s",s+1),len=strlen(s+1),cin>>t>>k,tot=la=1; 41 for (RG int i=1;i<=len;++i) add(s[i]-a); 42 for (RG int i=1;i<=tot;++i) ++c[l[i]]; 43 for (RG int i=1;i<=tot;++i) c[i]+=c[i-1]; 44 for (RG int i=1;i<=tot;++i) id[c[l[i]]--]=i; 45 if (t) for (RG int i=tot;i;--i) sz[fa[id[i]]]+=sz[id[i]]; 46 else for (RG int i=tot;i;--i) sz[id[i]]=1; sz[1]=0; 47 for (RG int i=tot;i;--i){ 48 sum[id[i]]=sz[id[i]]; 49 for (RG int j=0;j<26;++j) 50 sum[id[i]]+=sum[ch[id[i]][j]]; 51 } 52 if (sum[1]<k) puts("-1"); else dfs(1,k); return 0; 53 }

bzoj3998 [TJOI2015]弦論