您的位置:首页 > 其它

[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 TJOI 后缀自动机