3998: [TJOI2015]弦论
题目:传送门
题解:
SAM的入门题目(很好的复习了SAM并加强Right集合的使用)
其实对于第K小的字符串直接从root开始一通DFS就好,因为son边是直接根据字符存的呀,相当于自带字典序,直接从‘a' 开始找。
一开始初始化一下从当前状态出发所能走的路径数(也就是能构成多少个字符串啦)
然后根据T= 0/1分情况来处理Right集合大小:
T== 0 :那Right集合大小就直接全部等于1咯
T== 1 : 直接累加啊(原始操作)
然后具体看代码注释吧:
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 struct SAM 8 { 9 int son[31],fail,dep; 10 }ch[1110000];int cnt,last,root,a[510000]; 11 void add(int k) 12 { 13 int x=a[k]; 14 int p=last,np=++cnt;ch[np].dep=k; 15 while(p && ch[p].son[x]==0)ch[p].son[x]=np,p=ch[p].fail; 16 if(p==0)ch[np].fail=root; 17 else 18 { 19 int q=ch[p].son[x]; 20 if(ch[p].dep+1==ch[q].dep)ch[np].fail=q; 21 else 22 { 23 int nq=++cnt; 24 ch[nq]=ch[q];ch[nq].dep=ch[p].dep+1; 25 ch[np].fail=ch[q].fail=nq; 26 while(p && ch[p].son[x]==q)ch[p].son[x]=nq,p=ch[p].fail; 27 } 28 } 29 last=np; 30 } 31 int len,T,k,Rsort[1110000],r[1110000],sa[1110000],sum[1110000]; 32 char s[510000]; 33 void dfs(int x) 34 { 35 if(k<=r[x])return ; 36 k-=r[x];//要从x继续往下搜,那么结束位置为x的子串一定会更优,那就减去这样的子串个数啊,r记录的就是这个 37 for(int i=0;i<=25;i++) 38 if(ch[x].son[i]!=0) 39 { 40 int y=ch[x].son[i]; 41 if(k>sum[y])k-=sum[y];//当前k如果比y可以走出来的路径还要多,那么第k小的一定不在y的路径上。由于是按照字典序小到大问的,那么y的路径一定比后面枚举的要优,那也要减,sum记录的就是可行路径数 42 else {printf("%c",i+'a');dfs(y);return ;} 43 } 44 } 45 int main() 46 { 47 cnt=0;root=last=++cnt; 48 scanf("%s",s+1);len=strlen(s+1); 49 for(int i=1;i<=len;i++)a[i]=s[i]-'a'; 50 for(int i=1;i<=len;i++)add(i); 51 scanf("%d%d",&T,&k); 52 for(int i=1;i<=cnt;i++)Rsort[ch[i].dep]++; 53 for(int i=1;i<=len;i++)Rsort[i]+=Rsort[i-1]; 54 for(int i=cnt;i>=1;i--)sa[Rsort[ch[i].dep]--]=i; 55 for(int i=1,p=root;i<=len;i++)p=ch[p].son[a[i]],r[p]++; 56 for(int i=cnt;i>=1;i--) 57 if(T==0)r[ch[sa[i]].fail]=1; 58 else r[ch[sa[i]].fail]+=r[sa[i]]; 59 r[root]=0; 60 for(int i=cnt;i>=1;i--) 61 { 62 int now=sa[i];sum[now]=r[now]; 63 for(int j=0;j<=25;j++)if(ch[now].son[j]!=0)sum[now]+=sum[ch[now].son[j]]; 64 } 65 if(k>sum[root])printf("-1\n"); 66 else dfs(root); 67 return 0; 68 }