bzoj3439: Kpm的MC密码(主席树+DFS序+字典树)

17 篇文章 0 订阅
4 篇文章 0 订阅

题目传送门
做这题有人跟我说用链表。处理相同的串。
网上都说要。。
其实不用吧。。记录每个串的结尾是在字典树上哪个点就行啊。
然后一个一个插啊。

解法:
因为是后缀所以到这建字典树。
然后kpm串肯定是子树的所有串。
那么用主席树维护子树第k小。
要求编号连续就套个dfs序就行了。

代码实现:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct Trie {int son[31];Trie() {memset(son,-1,sizeof(son));}}tr[310000];
int tot,end[110000],root;char ss[310000];
void Build(int k) {
    int x=root,len=strlen(ss+1);
    for(int i=len;i>=1;i--) {
        int y=ss[i]-'a'+1;
        if(tr[x].son[y]==-1) {tot++;tr[x].son[y]=tot;}
        x=tr[x].son[y];
    }
    end[k]=x;
}
struct node {int lc,rc,c;}t[2100000];int cnt,rt[110000];
void build(int &u,int l,int r,int p,int c) {
    if(u==0)u=++cnt;t[u].c+=c;
    if(l==r)return ;int mid=(l+r)/2;
    if(p<=mid)build(t[u].lc,l,mid,p,c);
    else build(t[u].rc,mid+1,r,p,c);
}
void Merge(int &u1,int u2) {
    if(u1==0){u1=u2;return ;}if(u2==0)return ;
    t[u1].c+=t[u2].c;
    Merge(t[u1].lc,t[u2].lc);Merge(t[u1].rc,t[u2].rc);
}
int z,st[310000],ed[310000];
void dfs(int x) {
    st[x]=++z;
    for(int i=1;i<=26;i++)if(tr[x].son[i]!=-1)dfs(tr[x].son[i]);
    ed[x]=z;
}
int find(int u1,int u2,int l,int r,int k) {
    if(t[u1].c-t[u2].c<k)return -1;
    if(l==r)return l;int mid=(l+r)/2;
    int c=t[t[u1].lc].c-t[t[u2].lc].c;
    if(k<=c)return find(t[u1].lc,t[u2].lc,l,mid,k);
    else return find(t[u1].rc,t[u2].rc,mid+1,r,k-c);
}
int find_l(int u1,int u2,int l,int r,int p) {
    if(l==r)return t[u1].c-t[u2].c;int mid=(l+r)/2;
    int c=t[t[u1].lc].c-t[t[u2].lc].c;
    if(p<=mid)return find(t[u1].lc,t[u2].lc,l,mid,p);
    else return find(t[u1].rc,t[u2].rc,mid+1,r,p)+c;
}
int main() {
    int n;scanf("%d",&n);root=tot=0;
    for(int i=1;i<=n;i++) {scanf("%s",ss+1);Build(i);}
    z=0;dfs(root);cnt=0;
    for(int i=1;i<=n;i++)build(rt[st[end[i]]],1,n,i,1);
    for(int i=1;i<=z;i++)Merge(rt[i],rt[i-1]);
    for(int i=1;i<=n;i++) {
        int x,k;scanf("%d",&k);x=end[i];printf("%d\n",find(rt[ed[x]],rt[st[x]-1],1,n,k));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值