BZOJ 3998 [TJOI2015]弦论 (后缀自动机)
题目大意:
给你一个字符串,求字典序第$k$小的串是什么
先建出$sam$和$parent$树
在$trs$图中,从根走向节点x的每一条路径都是x能代表的一个字符串
$parent$树以 某个节点为根的子树内$endpos$节点的数量,表示这个节点 能代表的所有字符串 正序作为后缀在所有前缀串中出现的次数
利用这个性质,我们在$trs$图中反向拓扑,就能累加出以某个节点x代表的某个字符串为前缀的子串总数量
因为从一个节点$x$沿一条正向边$trs[x][i]$走向一个节点$y$,我们能表示的字符串就是在$x$原来表示的某个字符串$S$之后添加一个字符$i$,是正序的
而反向拓扑是统计$S$后面能接的串$ixxx$的总数量
最后在$trs$图上26分即可
1 #include <vector> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define N1 505000 6 #define S1 (N1<<1) 7 #define ll long long 8 #define uint unsigned int 9 #define rint register int 10 #define il inline 11 #define inf 0x3f3f3f3f 12 #define idx(X) (X-'a') 13 using namespace std; 14 15 int gint() 16 { 17 int ret=0,fh=1;char c=getchar(); 18 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 19 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 20 return ret*fh; 21 } 22 23 int T,K,n,m,len,cte; 24 char str[N1]; 25 26 namespace SAM{ 27 int trs[S1][26],pre[S1],dep[S1],ed[S1],la,tot; 28 ll sz[S1],sum[S1]; 29 void init(){tot=la=1;} 30 void Build_SAM(int x) 31 { 32 int p,q,np,nq; 33 np=++tot,p=la,la=np; 34 dep[np]=dep[p]+1,ed[np]=1; 35 for(;p&&!trs[p][x];p=pre[p]) trs[p][x]=np; 36 if(!p) pre[np]=1; 37 else{ 38 q=trs[p][x]; 39 if(dep[q]==dep[p]+1) pre[np]=q; 40 else{ 41 pre[nq=++tot]=pre[q]; 42 pre[np]=pre[q]=nq; 43 dep[nq]=dep[p]+1; 44 memcpy(trs[nq],trs[q],sizeof(trs[q])); 45 for(;p&&trs[p][x]==q;p=pre[p]) trs[p][x]=nq; 46 } 47 } 48 } 49 bool cmp(int x,int y){return x<y;} 50 51 int ret[N1],tp; 52 int hs[S1],que[S1]; 53 void solve() 54 { 55 init(); 56 for(int i=1;i<=len;i++) 57 Build_SAM(idx(str[i])); 58 for(int i=2;i<=tot;i++) 59 hs[dep[i]]++; 60 for(int i=1;i<=len;i++) 61 hs[i]+=hs[i-1]; 62 for(int i=1;i<=tot;i++) 63 que[hs[dep[i]]--]=i; 64 int x; 65 for(int i=tot-1;i>=1;i--) 66 { 67 x=que[i]; 68 sz[x]+=ed[x]?1:0; 69 if(!T) sz[x]=1; 70 else sz[pre[x]]+=sz[x]; 71 } 72 sz[1]=0; 73 for(int i=tot-1;i>=0;i--) 74 { 75 x=que[i]; 76 sum[x]+=sz[x]; 77 for(int j=0;j<26;j++) 78 sum[x]+=sum[trs[x][j]]; 79 } 80 x=1; 81 if(K>sum[1]){printf("-1\n");return;} 82 while(1) 83 { 84 if(K<=sz[x]) {break;} 85 K-=sz[x]; 86 for(int i=0;i<26;i++) 87 { 88 if(K>sum[trs[x][i]]){ 89 K-=sum[trs[x][i]]; 90 }else{ 91 ret[++tp]=i; 92 x=trs[x][i]; 93 break; 94 } 95 } 96 } 97 for(int i=1;i<=tp;i++) 98 printf("%c",ret[i]+'a'); 99 puts(""); 100 } 101 }; 102 103 int main() 104 { 105 //freopen("t2.in","r",stdin); 106 scanf("%s",str+1); 107 len=strlen(str+1); 108 scanf("%d%d",&T,&K); 109 SAM::solve(); 110 return 0; 111 }