BZOJ3439: Kpm的MC密码

【传送门:BZOJ3439


简要题意:

  给出n个字符串,给出ki,求出以每个字符串为后缀的字符串中的编号为ki的字符串,如果没有则输出-1


题解:

  倒着把字符串插进字典树里,这样子就可以保证一个点是它的子树的所有点的后缀(字典树里一个点代表一个字符串)

  然后跑一遍dfs,求dfs序,然后用主席树求出每个字符串的最下面的点的子树内第k小的值就行了


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct trnode
{
    int lc,rc,c;
}tr[11000000];int trlen,rt[810000];
void Link(int &u,int l,int r,int p)
{
    if(u==0) u=++trlen;
    tr[u].c++;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(p<=mid) Link(tr[u].lc,l,mid,p);
    else Link(tr[u].rc,mid+1,r,p);
}
void Merge(int &u1,int u2)
{
    if(u1==0){u1=u2;return ;}
    if(u2==0) return ;
    tr[u1].c+=tr[u2].c;
    Merge(tr[u1].lc,tr[u2].lc);
    Merge(tr[u1].rc,tr[u2].rc);
}
int findkth(int u1,int u2,int l,int r,int k)
{
    if(l==r) return l;
    if(tr[u1].c-tr[u2].c<k) return -1;
    int c=tr[tr[u1].lc].c-tr[tr[u2].lc].c,mid=(l+r)/2;
    if(c>=k) return findkth(tr[u1].lc,tr[u2].lc,l,mid,k);
    else return findkth(tr[u1].rc,tr[u2].rc,mid+1,r,k-c);
}
struct trie
{
    int c[27],s;
    trie()
    {
        s=0;
        memset(c,-1,sizeof(c));
    }
}t[810000];int tot;
char st[810000];
int L[810000],R[810000],ys[810000],z;
int w[810000];
int n;
int pre[810000];
void bt(int d)
{
    int x=0,len=strlen(st+1);
    for(int i=len;i>=1;i--)
    {
        int y=st[i]-'a'+1;
        if(t[x].c[y]==-1) t[x].c[y]=++tot;
        x=t[x].c[y];
    }
    if(t[x].s==0) t[x].s=d;
    else pre[d]=t[x].s,t[x].s=d; 
    w[d]=x;
}
void dfs(int x)
{
    L[x]=++z;ys[x]=z;
    if(t[x].s!=0)
    {
        Link(rt[z],1,n,t[x].s);
        int j=t[x].s;
        while(pre[j]!=0)
        {
            j=pre[j];
            Link(rt[z],1,n,j);
        }
    }
    for(int i=1;i<=26;i++)
    {
        if(t[x].c[i]!=-1)
        {
            dfs(t[x].c[i]);
        }
    }
    R[x]=z;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",st+1);
        bt(i);
    }
    z=0;dfs(0);
    for(int i=1;i<=z;i++) Merge(rt[i],rt[i-1]);
    for(int i=1;i<=n;i++)
    {
        int k;
        scanf("%d",&k);
        printf("%d\n",findkth(rt[R[w[i]]],rt[L[w[i]]-1],1,n,k));
    }
    return 0;
}

 

posted @ 2018-04-17 15:12  Star_Feel  阅读(191)  评论(0编辑  收藏  举报