BZOJ3998 TJOI2015 弦论 后缀自动机
题意:求一个字符串的第K小字串,T=0表示不同位置相同的子串算作一个,T=1算作多个
题意:
建出SAM来跑第K子串,由于一个点所代表的子串在原串出现次数为其子树叶子结点的数量,因而有:
T==1,每个点的|right|=1
T==2,每个点的|right|=子树叶子结点数
BFS跑出所有子串出现的次数即|right|,DFS统计每个节点的出现次数和,具体细节看代码。
然后处理询问即可。
#include <cstdio> #include <cstring> #include <cstdlib> #include <climits> #include <iostream> #include <algorithm> using namespace std; const int MAXK=26; const int MAXN=500000+2; struct SAM{ int v,c,s,t; SAM *child[MAXK+2],*f; SAM(){} SAM(int _v):v(_v),f(0),c(0),s(0),t(0){ memset(child,0,sizeof(child));} }*root,*last=root=new SAM(0),*q[2*MAXN]; int N,K,cnt; bool T; char S[MAXN]; void Extend(SAM *&x,int c){ SAM *p=last,*np=new SAM(p->v+1),*q,*nq; while(p && !p->child[c]) p->child[c]=np,p=p->f; if(!p) np->f=x; else{ q=p->child[c]; if(q->v==p->v+1) np->f=q; else{ nq=new SAM(p->v+1); memcpy(nq->child,q->child,sizeof(q->child)); nq->f=q->f,q->f=np->f=nq; while(p && p->child[c]==q) p->child[c]=nq,p=p->f; } } last=np; } void BFS(SAM *&root){ SAM *x; int l=0,r=1; q[0]=root; while(l!=r){ x=q[l++]; for(int i=0;i<MAXK;i++) if(x->child[i] && !x->child[i]->t) x->child[i]->t=1,q[r++]=x->child[i]; } for(int i=r-1;i;i--){ if(!T && q[i]->c) q[i]->c=1; q[i]->f->c+=q[i]->c; } } void DFS(SAM *&x){ x->t=2,x->s=x->c; for(int i=0;i<MAXK;i++) if(x->child[i]){ if(x->child[i]->t!=2) DFS(x->child[i]); x->s+=x->child[i]->s; } } void Query(SAM *&x,int k){ if(k<=x->c) return; k-=x->c; for(int i=0;i<MAXK;i++) if(x->child[i]){ if(k<=x->child[i]->s){ S[++cnt]='a'+i,Query(x->child[i],k); return; } k-=x->child[i]->s; } } int main(){ scanf("%s %d %d",S,&T,&K),N=strlen(S); for(int i=0;i<N;i++) Extend(root,S[i]-'a'),last->c++; BFS(root),root->c=0,DFS(root); if(root->s<K) cout << -1 << endl; else{ memset(S,0,sizeof(S)); Query(root,K); printf("%s\n",S+1); } return 0; }