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 }
原文地址:https://www.cnblogs.com/lidaxin/p/5200675.html