给一个字符串S,若干个查询,每个查询k输出字典序第k小的S的子串。把字符串一次插入到自动机中,由于在不考虑父边的情况下,可以看做一个有向无环图,那么我们可在在这个图上进行dp,令dp[i]表示从节点i开始有多少个子串,转移dp[i]=dp[j]+1(存在一条边c使得i-->j),然后就是从根节点贪心走一遍就可以得到要求的串了。
另外吐槽一句SPOJ上的题卡时间卡的太凶残了....
#include <iostream> #include <cstdio> #include <memory.h> #include <algorithm> #include <string> #include <cstring> #include <stack> using namespace std; const int maxn=180010; const int S=26; int next[maxn][26]; char ch[maxn][26]; char str[maxn>>1]; int wtop[maxn]; int cnt[maxn]; int n,m,q; int len,tot; struct node { node *par,*go[S]; int val,cnt; }*root,*tail,que[maxn],*top[maxn]; char ans[maxn]; int c[maxn]; inline int idx(char c) { return c-'a'; } inline void add(int c,int l) { node *p=tail; node *np=&que[tot++]; np->val=l; while (p && p->go[c]==NULL) p->go[c]=np,p=p->par; if (p==NULL) np->par=root; else { node *q=p->go[c]; if (p->val+1==q->val) np->par=q; else { node *nq=&que[tot++]; *nq=*q; nq->val=p->val+1; np->par=q->par=nq; while (p&&p->go[c]==q) p->go[c]=nq,p=p->par; } } tail=np; } inline void init() { //memset(que,0,sizeof que); len=1; tot=0; root=tail=&que[tot++]; } inline void slove(int k) { int now=0; int l=0; while (k) { for (int i=0; i<cnt[now]; i++) { if (k>que[next[now][i]].cnt) k-=que[next[now][i]].cnt; else { ans[l++]=ch[now][i]; k--; now=next[now][i]; break; } } } ans[l]='\0'; puts(ans); } int main() { //freopen("a.txt","r",stdin); //freopen("out.txt","w",stdout); gets(str); init(); int l=strlen(str); for (int i=0; i<l; i++) add(idx(str[i]),len++); // memset(c,0,sizeof c); // memset(wtop,0,sizeof wtop); // memset(cnt,0,sizeof cnt); for (int i=0; i<tot; i++) c[que[i].val]++; for (int i=1; i<len; i++) c[i]+=c[i-1]; for (int i=0; i<tot; i++) { top[--c[que[i].val]]=&que[i]; wtop[c[que[i].val]]=i; } for (int i=0; i<tot; i++) que[i].cnt=1; for (int i=tot-1; i>=0; i--) { node *p=top[i]; for (int j=0; j<S; j++) { if (p->go[j]) { int u=p-que,v=p->go[j]-que; next[u][cnt[u]]=v; ch[u][cnt[u]++]=j+'a'; p->cnt+=p->go[j]->cnt; } } } scanf("%d",&q); while (q--) { scanf("%d",&m); slove(m); } return 0; }