标签:第k小 put class break ace dfs bre 可持久化线段树 last
题意:
对于一个给定长度为N的字符串,求它的第K小子串是什么。N<=5000000,K<=1000000000。
题解:
对于本题,首先我们要做的事情是先建立后缀自动机。
如果T=0,那么每一个位置的出现次数直接设为1,T=1否则就是正常的right集合大小。
那么我们可以再记一个sum[i],表示从i出发能走多少个串。
那么我们只需要从a~z依次枚举下一个位置的字母,然后类似于可持久化线段树那样判断一下k是否小于等于sum[nex[i]],如果是就走它,否则k减掉他,继续枚举下一个字母,当k<=当前位置代表的字符串出现的次数的时候停下来即可。
#include<algorithm> #include<cstdlib> #include<cstring> #include<cstdio> using namespace std; char s[500002]; int cur=1,cnt=1,n,t,k,last,ch[1000002][27],fa[1000002],dis[1000002],q[500002],st[1000002]; long long sum[1000002],v[1000002]; bool fl[1000002]; void build(int c,int id){ last=cur;cur=++cnt; int p=last;dis[cur]=id; for (;p&&!ch[p][c];p=fa[p])ch[p][c]=cur; if (!p)fa[cur]=1; else { int q=ch[p][c]; if (dis[q]==dis[p]+1)fa[cur]=q; else { int nt=++cnt;dis[nt]=dis[p]+1; for (int i=0;i<26;i++) ch[nt][i]=ch[q][i]; fa[nt]=fa[q];fa[q]=fa[cur]=nt; for (;ch[p][c]==q;p=fa[p])ch[p][c]=nt; } } v[cur]=1; } void dfs(int x){ if (fl[x])return; fl[x]=1;sum[x]=v[x]; for (int i=0;i<26;i++) if (ch[x][i]){dfs(ch[x][i]);sum[x]+=sum[ch[x][i]];} } int main() { scanf("%s%d%d",s+1,&t,&k);n=strlen(s+1); for(int i=1;i<=n;i++)build(s[i]-‘a‘,i); for (int i=1;i<=cnt;i++)q[dis[i]]++; for (int i=1;i<=n;i++)q[i]+=q[i-1]; for (int i=1;i<=cnt;i++)st[q[dis[i]]--]=i; for (int i=cnt;i>=1;i--) { if (t==1)v[fa[st[i]]]+=v[st[i]]; else v[i]=1; } v[1]=0;dfs(1); if (sum[1]<k){puts("-1");return 0;} int rt=1; while(1) { if (k<=v[rt])break;else k-=v[rt]; bool u=0;int son; for (int i=0;i<26;i++) if (ch[rt][i]) { u=1; if (sum[ch[rt][i]]<k)k-=sum[ch[rt][i]]; else {printf("%c",i+‘a‘);son=ch[rt][i];break;} } if (!u)break; rt=son; } return 0; }
标签:第k小 put class break ace dfs bre 可持久化线段树 last
原文地址:https://www.cnblogs.com/1124828077ccj/p/12302915.html