bzoj 3998: [TJOI2015]弦论
Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input
第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
Sample Input
aabc
0 3
0 3
Sample Output
aab
HINT
N<=5*10^5
T<2
K<=10^9
构建出SAM之后,求出sum[i],表示有sum[i]个子串经过i号点
siz[i]表示ii所代表的Endpos的集合大小,也就是i所对应字符串集合的出现次数
T=0时,本质相同的子串在不同位置出现算相同,所以siz[i]=1,即将每个字符串集合的Endpos集合大小(字符串集合元素出现次数)置为1
T=1时,本质相同的子串在不同位置出现算不同,那么累加后的siz表示实际上Endpos的集合大小
1 #include<bits/stdc++.h> 2 using namespace std; 3 int const N=500000+1000; 4 struct node{ 5 int len,fa,ch[26]; 6 node() { memset(ch,0,sizeof(ch)); len=0; } 7 }a[N<<1]; 8 char s[N]; 9 long long sum[N<<1]; 10 int sz[N<<1],t,k,n,ls=1,tot=1,num[N],sa[N<<1]; 11 void add(int c){ 12 int p=ls; 13 int np=ls=++tot; 14 sz[tot]=1; 15 a[np].len=a[p].len+1; 16 for(;p&&!a[p].ch[c]; p=a[p].fa) a[p].ch[c]=np; 17 if(!p) a[np].fa=1; 18 else { 19 int q=a[p].ch[c]; 20 if(a[q].len==a[p].len+1) a[np].fa=q; 21 else { 22 int nq=++tot; a[nq]=a[q]; 23 a[nq].len=a[p].len+1; 24 a[q].fa=a[np].fa=nq; 25 for(;p && a[p].ch[c]==q; p=a[p].fa) a[p].ch[c]=nq; 26 } 27 } 28 } 29 void dfs(int x,int k){ 30 if(sz[x]>=k) return; 31 k-=sz[x]; 32 for(int i=0;i<26;i++) { 33 int v=a[x].ch[i]; 34 if(!v) continue; 35 if(k>sum[v]) { 36 k-=sum[v]; continue; 37 } 38 putchar('a'+i); 39 dfs(v,k); 40 return; 41 } 42 } 43 int main(){ 44 scanf("%s",s); 45 n=strlen(s); 46 scanf("%d%d",&t,&k); 47 for(int i=0;s[i];i++) add(s[i]-'a'); 48 for(int i=1;i<=tot;i++) num[a[i].len]++; 49 for(int i=1;i<=n;i++) num[i]+=num[i-1]; 50 for(int i=1;i<=tot;i++) sa[num[a[i].len]--]=i; 51 for(int i=tot;i>=1;i--) sz[a[sa[i]].fa]+=sz[sa[i]]; 52 for(int i=1;i<=tot;i++) 53 if(t==0) sum[i]=sz[i]=1; 54 else sum[i]=sz[i]; 55 sum[1]=sz[1]=0; 56 for(int i=tot;i>=1;i--) 57 for(int j=0;j<26;j++) 58 if(a[sa[i]].ch[j]) sum[sa[i]]+=sum[a[sa[i]].ch[j]]; 59 if(sum[1]<k) puts("-1"); 60 else dfs(1,k); 61 return 0; 62 }