[BZOJ3998][TJOI2015]弦论(后缀自动机)
2017-03-21 08:19
447 查看
=== ===
这里放传送门=== ===
题解
求后缀自动机上的第K小子串,基本思路是建立自动机以后递推出从每个节点出发还能到达多少子串,通过这个值来判断在每个节点的时候需要走哪一个儿子。对于这个题来说type=0或=1的时候有一点点区别。type为0的时候相同的子串算作一个,那么就只需要统计在自动机上从当前节点出发有多少条不同的路径就可以了,实际上就是一个拓扑图上的递推;type为1的时候相同的子串算作多个,那么每个节点贡献的路径数目就不只是1而是它Right集合的大小。代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int len,tot,b[1100010],d[1100010],T,K; char s[500010],ans[500010]; struct Node{ Node *ch[30],*fa; int step; long long res,cnt; Node(); }*null,*Root,*t[1100010],P[1100010],*last,*p,*q,*np,*nq; Node::Node(){ for (int i=0;i<=26;i++) ch[i]=null; fa=null;step=res=cnt=0; } Node *New(){return t[++tot];} void insert(int c){ p=last;np=last=New(); np->step=p->step+1; while (p->ch[c]==null&&p!=null){ p->ch[c]=np;p=p->fa; } if (p==null){np->fa=Root;return;} q=p->ch[c]; if (q->step==p->step+1){np->fa=q;return;} nq=New();nq->step=p->step+1; memcpy(nq->ch,q->ch,sizeof(q->ch)); nq->fa=q->fa;q->fa=np->fa=nq; while (p->ch[c]==q){p->ch[c]=nq;p=p->fa;} } void getord(){ for (int i=1;i<=tot;i++) ++b[t[i]->step]; for (int i=1;i<=tot;i++) b[i]+=b[i-1]; for (int i=tot;i>=1;i--) d[b[t[i]->step]--]=i; } void getres(){ for (int i=tot;i>=1;i--){ Node *v=t[d[i]]; v->res++;v->cnt=1; for (int j=0;j<26;j++) v->res+=v->ch[j]->res; } } void getcnt(){ Node *ptr=Root; for (int i=0;i<len;i++){ ptr=ptr->ch[s[i]-'a']; ptr->cnt++; } for (int i=tot;i>=1;i--){ Node *v=t[d[i]]; v->fa->cnt+=v->cnt; } for (int i=tot;i>=1;i--){ Node *v=t[d[i]]; v->res=v->cnt;//用cnt来递推能够到达的重复子串的个数 for (int j=0;j<26;j++) v->res+=v->ch[j]->res; } } void search(Node *now,int k){ if (k<=0) return; for (int i=0;i<26;i++) if (now->ch[i]!=null){ Node *v=now->ch[i]; if (k<=v->res){ ans[++len]=i+'a'; search(v,k-v->cnt);break;//注意转移的时候要减去当前的cnt }else k-=v->res; } } int main() { null=new Node;*null=Node(); gets(s);len=strlen(s); for (int i=0;i<=2*len+10;i++){ P[i]=Node();t[i]=P+i; } last=Root=New(); for (int i=0;i<len;i++) insert(s[i]-'a'); getord(); scanf("%d%d",&T,&K); if (T==0) getres(); else getcnt(); len=0;search(Root,K); if (len==0) printf("-1"); for (int i=1;i<=len;i++) printf("%c",ans[i]); printf("\n"); return 0; }
偏偏在最后出现的补充说明
好久不做后缀自动机好多东西都忘得差不多了。。常见的几种递推比如Right集合啊,还有这个题的能到达多少子串啊之类的。。
相关文章推荐
- 【后缀自动机】 BZOJ 3998: [TJOI2015]弦论
- BZOJ 3998: [TJOI2015]弦论 后缀自动机
- bzoj 3998: [TJOI2015]弦论 后缀自动机
- bzoj 3998 [TJOI2015]弦论 后缀数组||后缀自动机
- 【BZOJ3998】[TJOI2015]弦论 后缀自动机
- BZOJ_3998_[TJOI2015]弦论_后缀自动机
- [BZOJ3998][TJOI2015]弦论(后缀自动机)
- [K大子串 后缀自动机 模板题] BZOJ 3998 [TJOI2015]弦论
- bzoj 3998: [TJOI2015]弦论(后缀自动机)
- bzoj3998 [TJOI2015]弦论 后缀自动机
- [BZOJ3998][TJOI2015]弦论 后缀自动机
- BZOJ 3998 TJOI 2015 弦论 后缀自动机
- BZOJ 题目3998: [TJOI2015]弦论(后缀自动机求排名第k的子串,可重复)
- bzoj 3998: [TJOI2015]弦论 (后缀自动机)
- [BZOJ3998][TJOI2015]弦论(后缀自动机)
- BZOJ.3998.[TJOI2015]弦论(后缀自动机)
- [BZOJ3998][TJOI2015]弦论 后缀自动机
- BZOJ3998 [TJOI2015]弦论 【后缀自动机】
- 【BZOJ3998】【TJOI2015】 弦论 后缀自动机
- 【BZOJ3998】【TJOI2015】弦论 后缀自动机