bzoj3998 [TJOI2015]弦论(SAM)
【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=3998
【题意】
询问排名第k的子串是谁,0代表相同子串不同位置算作相同,1代表相同子串不同位置算作不同。
【思路】
0的情况和这个题一样每个子串不同位置出现次数算作1;
至于1,统计val作为该子串在不同位置出现的次数,就是求一下|right|的大小呗,相应修改一下就可以了。
【代码】
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 5 const int N = 5*1e5+10; 6 7 char s[N]; 8 int root,last,sz,ch[N][26],fa[N],sum[N],val[N],l[N]; 9 void add(int x) { 10 int c=s[x]-'a'; 11 int p=last,np=++sz; last=np; 12 l[np]=x+1; val[np]=1; 13 for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; 14 if(!p) fa[np]=root; 15 else { 16 int q=ch[p][c]; 17 if(l[p]+1==l[q]) fa[np]=q; 18 else { 19 int nq=++sz; l[nq]=l[p]+1; 20 memcpy(ch[nq],ch[q],sizeof(ch[q])); 21 fa[nq]=fa[q]; 22 fa[np]=fa[q]=nq; 23 for(;p&&q==ch[p][c];p=fa[p]) ch[p][c]=nq; 24 } 25 } 26 } 27 28 int n,T,b[N],cnt[N]; 29 30 void get_sum() { 31 for(int i=1;i<=sz;i++) cnt[l[i]]++; 32 for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1]; 33 for(int i=sz;i>=1;i--) b[cnt[l[i]]--]=i; 34 for(int i=sz;i;i--) 35 if(!T) val[b[i]]=1; 36 else val[fa[b[i]]]+=val[b[i]]; 37 val[1]=0; 38 for(int i=sz;i;i--) { 39 int p=b[i]; sum[p]=val[p]; 40 for(int j=0;j<26;j++) 41 sum[p]+=sum[ch[p][j]]; 42 } 43 } 44 45 int main() { 46 scanf("%s",s); 47 n=strlen(s); 48 root=last=++sz; 49 for(int i=0;i<n;i++) add(i); 50 scanf("%d",&T); 51 int x,p=root; scanf("%d",&x); 52 get_sum(); 53 if(x>sum[1]) { puts("-1");return 0; } 54 while(x>val[p]) { 55 for(int c=0;c<26;c++)if(ch[p][c]) { 56 if(sum[ch[p][c]]>=x) { 57 putchar('a'+c); 58 x-=val[p]; p=ch[p][c]; 59 break; 60 } 61 else x-=sum[ch[p][c]]; 62 } 63 } 64 return 0; 65 }
posted on 2016-02-19 13:06 hahalidaxin 阅读(366) 评论(0) 编辑 收藏 举报