bzoj3998 [TJOI2015]弦论
题目大意:
求第k小的子串。
解题方法:
首先构建后缀自动机,通过拓扑排序求出每个子串的cnt(即该串的出现次数)。然后跑DFS,类似于平衡树求rank为k的数是什么的操作,如果当前指向的儿子(假装他是儿子)的cnt小于k,则k-=cnt,并转向下一个儿子。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<iostream> 7 #include<queue> 8 #define ll long long 9 #define del(a,b) memset(a,b,sizeof(a)) 10 using namespace std; 11 const int MAXN=1e6+10; 12 int op; 13 int c[MAXN],a[MAXN]; 14 char s[MAXN]; 15 struct SAM 16 { 17 struct State 18 { 19 int ch[26],fa,val; 20 }t[MAXN]; 21 int n,sz,root,last; 22 int cnt[MAXN],sum[MAXN]; 23 int nw(int x){t[++sz].val=x;return sz;} 24 void init() 25 { 26 sz=0;root=last=nw(0); 27 del(cnt,0);del(sum,0); 28 } 29 void extend(int c) 30 { 31 int p=last,np=nw(t[p].val+1); 32 for(;p&&!t[p].ch[c];p=t[p].fa) t[p].ch[c]=np; 33 if(!p) t[np].fa=root; 34 else 35 { 36 int q=t[p].ch[c]; 37 if(t[q].val==t[p].val+1) t[np].fa=q; 38 else 39 { 40 int nq=nw(t[p].val+1); 41 memcpy(t[nq].ch,t[q].ch,sizeof(t[q].ch)); 42 t[nq].fa=t[q].fa; 43 t[q].fa=t[np].fa=nq; 44 for(;p&&t[p].ch[c]==q;p=t[p].fa) t[p].ch[c]=nq; 45 } 46 } 47 cnt[np]=1;last=np; 48 } 49 void RadixSort() 50 { 51 del(c,0); 52 for(int i=1;i<=sz;i++) c[t[i].val]++; 53 for(int i=1;i<=n;i++) c[i]+=c[i-1]; 54 for(int i=sz;i>=1;i--) a[c[t[i].val]--]=i; 55 } 56 void get_pre() 57 { 58 for(int i=sz;i>=1;i--) 59 if(op==1) cnt[t[a[i]].fa]+=cnt[a[i]]; 60 else cnt[a[i]]=1; 61 cnt[1]=0; 62 for(int i=sz;i>=1;i--) 63 { 64 sum[a[i]]=cnt[a[i]]; 65 for(int j=0;j<26;j++) sum[a[i]]+=sum[t[a[i]].ch[j]]; 66 } 67 } 68 void DFS(int x,int k) 69 { 70 if(k<=cnt[x]) return; 71 k-=cnt[x]; 72 for(int i=0;i<26;i++) 73 if(int to=t[x].ch[i]) 74 { 75 if(k<=sum[to]) 76 { 77 putchar(i+'a'); 78 DFS(to,k);return; 79 } 80 k-=sum[to]; 81 } 82 } 83 }sam; 84 int main() 85 { 86 int k;sam.init(); 87 scanf("%s",s+1); 88 sam.n=strlen(s+1); 89 scanf("%d%d",&op,&k); 90 for(int i=1;i<=sam.n;i++) sam.extend(s[i]-'a'); 91 sam.RadixSort();sam.get_pre(); 92 if(k>sam.sum[1]) puts("-1"); 93 else sam.DFS(1,k); 94 return 0; 95 }