[后缀自动机][TJOI2015]弦论
题目描述
为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?
输入输出格式
输入格式:第一行是一个仅由小写英文字母构成的字符串s
第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。k的意义见题目描述。
输出格式:输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。
输入输出样例
输入样例#1:
aabc 0 3
输出样例#1:
aab
输入样例#2:
aabc 1 3
输出样例#2:
aa
输入样例#3:
aabc 1 11
输出样例#3:
-1
说明
数据范围
对于10%的数据,n ≤ 1000。
对于50%的数据,t = 0。
对于100%的数据,n ≤ 5 × 10^5, t < 2, k ≤ 10^9。
题解
构建出SAM之后,求出sum[i],表示有sum[i]个子串经过i号点
siz[i]表示i所代表的Endpos的集合大小,也就是i所对应字符串集合的出现次数
T=0时,本质相同的子串在不同位置出现算相同,所以siz[i]=1,即将每个字符串集合的Endpos集合大小(字符串集合元素出现次数)置为1
T=1时,本质相同的子串在不同位置出现算不同,那么累加后的siz表示实际上Endpos的集合大小
siz是在parent树上统计的 sum是在后缀自动机的DAG上拓扑逆序求得的
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int N=500000+15; 5 int n,tot=1,las=1,siz[2*N]; 6 struct point{int ch[26],ff,len;}t[2*N]; 7 inline void extend(int c) 8 {int p=las,np=++tot; siz[tot]=1; las=np; 9 t[np].len=t[p].len+1; 10 while(p&&!t[p].ch[c]) t[p].ch[c]=np,p=t[p].ff; 11 if(!p) t[np].ff=1; 12 else 13 {int q=t[p].ch[c]; 14 if(t[p].len+1==t[q].len) t[np].ff=q; 15 else 16 {int nq=++tot; t[nq]=t[q]; t[nq].len=t[p].len+1; 17 t[q].ff=t[np].ff=nq; 18 while(p&&t[p].ch[c]==q) t[p].ch[c]=nq,p=t[p].ff; 19 } 20 } 21 } 22 char s[N]; 23 int num,last[N*2],nxt[2*N],ver[2*N]; 24 inline void add(int x,int y) {nxt[++num]=last[x]; last[x]=num; ver[num]=y;} 25 int sum[2*N],tong[2*N],ord[2*N]; 26 27 int main() 28 {int ti,k; 29 scanf("%s%d%d",s+1,&ti,&k); int len=strlen(s+1); 30 for(int i=1;i<=len;i++) extend(s[i]-'a'); 31 32 for(int i=2;i<=tot;i++) add(t[i].ff,i); 33 34 35 for(int i=1;i<=tot;i++) tong[t[i].len]++; 36 for(int i=1;i<=len;i++) tong[i]+=tong[i-1]; 37 for(int i=1;i<=tot;i++) ord[tong[t[i].len]--]=i; 38 for(int i=tot;i;i--) 39 if(ti) siz[t[ord[i]].ff]+=siz[ord[i]]; 40 else siz[ord[i]]=1; 41 siz[1]=sum[1]=0; 42 43 44 45 for(int i=tot;i;i--) 46 {sum[ord[i]]=siz[ord[i]]; 47 for(int j=0;j<26;j++) 48 sum[ord[i]]+=sum[t[ord[i]].ch[j]]; 49 } 50 51 //for(int i=1;i<=tot;i++) printf("%d ",siz[i]); printf("\n"); 52 // for(int i=1;i<=tot;i++) printf("%d ",sum[i]); 53 if(k>sum[1]) {printf("-1"); return 0;} 54 int now=1; 55 while((k-=siz[now])>0) 56 {int p=0; 57 while(k>sum[t[now].ch[p]]) k-=sum[t[now].ch[p++]]; 58 now=t[now].ch[p]; 59 printf("%c",'a'+p); 60 } 61 return 0; 62 }