BZOJ 3998 TJOI2015 弦论 后缀自动机+DAG上的dp

Posted KKKorange的代码盒子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 3998 TJOI2015 弦论 后缀自动机+DAG上的dp相关的知识,希望对你有一定的参考价值。

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3998

 

题意概述:对于一个给定长度为N的字符串,求它的第K小子串是什么,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。N<=500000,K<=10^9.

 

应该是有三种做法的(当然后缀树我还没有看),于是就把这个东西当成后缀自动机的板子了(因为它很裸啊!!!)。

可以注意到当T=0的时候每走动一步的贡献是1,而T=1的时候每走动一步之后的贡献都是走到的这个状态的right集合大小。同时从同一个状态走出去得到的字符串又有很多种,不难想到可以搞个dp表示从这个点出发(包括这个点自己)可以找到的不同子串数(根据T而定)。

有个技巧是根据MAX把所有的状态排序之后就可以欢快地topo图上转移dp啦!(SAM的转移图是一张DAG)

所以还是好好理解一下SAM上的状态和转移吧,,,。。。。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 const int MAXN=500005;
14 
15 int T,K,n; char S[MAXN];
16 
17 struct SAM{
18     static const int maxn=500005;
19     static const int sigma_sz=26;
20     int pa[maxn<<1],to[maxn<<1][sigma_sz],mx[maxn<<1],val[maxn<<1],cnt[maxn<<1];
21     int sz,last,c[maxn],a[maxn<<1];
22     SAM(){ last=sz=1; memset(to[1],0,sizeof(to[1])); }
23     int newnode(){
24         memset(to[++sz],0,sizeof(to[sz]));
25         pa[sz]=mx[sz]=val[sz]=cnt[sz]=0;
26         return sz;
27     }
28     void extend(int w){
29         int p=last,np=newnode(); last=np;
30         pa[np]=mx[np]=cnt[np]=0;
31         mx[np]=mx[p]+1,val[np]=1;
32         while(p&&!to[p][w]) to[p][w]=np,p=pa[p];
33         if(!p) pa[np]=1;
34         else{
35             int q=to[p][w];
36             if(mx[p]+1==mx[q]) pa[np]=q;
37             else{
38                 int nq=newnode(); mx[nq]=mx[p]+1;
39                 memcpy(to[nq],to[q],sizeof(to[nq]));
40                 pa[nq]=pa[q];
41                 pa[np]=pa[q]=nq;
42                 while(p&&to[p][w]==q) to[p][w]=nq,p=pa[p];
43             }
44         }
45     }
46     void ready(){
47         for(int i=1;i<=sz;i++) c[mx[i]]++;
48         for(int i=1;i<=n;i++) c[i]+=c[i-1];
49         for(int i=sz;i>0;i--) a[c[mx[i]]--]=i;
50         for(int i=sz;i>0;i--){
51             if(T) val[pa[a[i]]]+=val[a[i]];
52             else val[a[i]]=1;
53         }
54         val[1]=0;
55         for(int i=sz;i>0;i--){
56             cnt[a[i]]=val[a[i]];
57             for(int j=0;j<26;j++) cnt[a[i]]+=cnt[to[a[i]][j]];
58         }
59     }
60     void dfs(int i){
61         if(val[i]>=K) return;
62         K-=val[i];
63         for(int j=0;j<26;j++) if(to[i][j]){
64             if(cnt[to[i][j]]>=K){
65                 putchar(j+a); dfs(to[i][j]);
66                 return;
67             }
68             K-=cnt[to[i][j]];
69         }
70     }
71 }sam;
72 
73 void data_in()
74 {
75     gets(S);
76     scanf("%d%d",&T,&K);
77 }
78 void work()
79 {
80     n=strlen(S);
81     for(int i=0;i<n;i++) sam.extend(S[i]-a);
82     sam.ready();
83     if(sam.cnt[1]<K) printf("%d\n",-1);
84     else sam.dfs(1);
85 }
86 int main()
87 {
88     data_in();
89     work();
90     return 0;
91 }

 

以上是关于BZOJ 3998 TJOI2015 弦论 后缀自动机+DAG上的dp的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj3998][TJOI2015]弦论——后缀自动机

●BZOJ 3998 [TJOI2015]弦论

BZOJ3998 TJOI2015 弦论 后缀自动机

bzoj3998 [TJOI2015]弦论

BZOJ3998 [TJOI2015]弦论 后缀自动机

bzoj3998[TJOI2015]弦论 后缀自动机+dp