http://www.lydsy.com/JudgeOnline/problem.php?id=3998 (题目链接)
题意
给出一个字符串,求它的字典序第K小的子串是什么,分情况讨论不在同一位置的相同子串需不需要重复考虑。
Solution
对于不需要重复考虑的情况,直接就是spoj上的那道例题,而需要重复考虑的情况不过就是预处理sum的时候每次加上的是当前节点的right集合大小。
代码
// bzoj3998 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<set> #define LL long long #define inf 1<<30 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=500010; int n,T,K; char s[maxn],ans[maxn]; namespace SAM { int last,Dargen,sz,n; int len[maxn<<1],ch[maxn<<1][26],par[maxn<<1]; int b[maxn],id[maxn<<1],sum[maxn<<1],r[maxn<<1]; void Extend(int c) { int np=++sz,p=last;last=np; len[np]=len[p]+1; for (;p && !ch[p][c];p=par[p]) ch[p][c]=np; if (!p) par[np]=Dargen; else { int q=ch[p][c]; if (len[q]==len[p]+1) par[np]=q; else { int nq=++sz;len[nq]=len[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); par[nq]=par[q]; par[np]=par[q]=nq; for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq; } } } void build() { last=Dargen=sz=1; n=strlen(s+1); for (int i=1;i<=n;i++) Extend(s[i]-'a'); } void pre() { for (int i=1;i<=sz;i++) b[len[i]]++; for (int i=1;i<=n;i++) b[i]+=b[i-1]; for (int i=1;i<=sz;i++) id[b[len[i]]--]=i; if (!T) { for (int i=1;i<=sz;i++) r[i]=1; for (int S=0,i=sz;i>=1;i--,S=0) { for (int j=0;j<26;j++) if (ch[id[i]][j]) S+=sum[ch[id[i]][j]]; sum[id[i]]=S+1; } } else { for (int p=Dargen,i=1;i<=n;i++) p=ch[p][s[i]-'a'],r[p]++; for (int i=sz;i>=1;i--) r[par[id[i]]]+=r[id[i]]; for (int S=0,i=sz;i>=1;i--,S=0) { for (int j=0;j<26;j++) if (ch[id[i]][j]) S+=sum[ch[id[i]][j]]; sum[id[i]]=S+r[id[i]]; } } } void query() { int tot=0,p=Dargen; while (K>0) { for (int i=0;i<26;i++) if (ch[p][i]) { if (sum[ch[p][i]]>=K) { p=ch[p][i];K-=r[p]; ans[++tot]=i+'a'; break; } else K-=sum[ch[p][i]]; } } ans[++tot]=' '; } } using namespace SAM; int main() { scanf("%s",s+1); scanf("%d%d",&T,&K); build(); pre(); query(); puts(ans+1); return 0; }