【BZOJ3439】Kpm的MC密码

题目链接:BZOJ3439

(tips:题目条件没给全,底下所有字符串的长度之和大约不超过 5 × 1 0 5 5×10^5 5×105)

解法:字典树+主席树

先设法找到一个求kpm串的方法。按读入的串反序构造字典树,称一个字符串的尾节点为其在树中的路径中深度最大的那个点,记为 t s t_s ts。那么一个字符串 s s s的所有kmp串的尾节点全部位于以 t s t_s ts为根的子树内。
题目转化为求任意子树内权值第 k k k大(注意有些节点不是任何字符串的尾节点,所以它的权值是空的,设成 + ∞ +\infty +)。
然后给所有节点编上dfs序,主席树维护即可。
时间复杂度 O ( m + ( m + n ) log ⁡ n ) O(m+(m+n)\log n) O(m+(m+n)logn) m m m为字符串总长)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
 
using namespace std;
 
const int N=13000001,M=100001,S=500001;
int root=1,tot,tot0=1,idx,dfn[M],lst[M],sum[N],lson[N],rson[N],son[S][26],pos[M],n,rt[S],k,nod[S];
char str[S];
vector<int> id[S];
 
inline int create(int x){sum[++tot]=sum[x],lson[tot]=lson[x],rson[tot]=rson[x];return tot;}
 
void insert(char *str,int j){
    int u=root,m=strlen(str+1);
    for(int i=m;i;--i){int &v=son[u][str[i]-'a'];if(!v)v=++tot0;u=v;}
    id[u].push_back(j),pos[j]=u;
}
 
void dfs(int u){
    nod[dfn[u]=++idx]=u;
    for(int i=0;i<26;++i)if(son[u][i])dfs(son[u][i]);
    lst[u]=idx;
}
 
void update(int &o,int p,int l,int r,int k,int x){
    o=create(p);
    if(l==r){sum[o]=x;return;}
    int m=l+r>>1;if(m>=k)update(lson[o],lson[p],l,m,k,x);else update(rson[o],rson[p],m+1,r,k,x);
    sum[o]=sum[lson[o]]+sum[rson[o]];
}
 
void update2(int &o,int l,int r,int k){
    if(!o)o=++tot;
    if(l==r){sum[o]=1;return;}
    int m=l+r>>1;if(m>=k)update2(lson[o],l,m,k);else update2(rson[o],m+1,r,k);
    sum[o]=sum[lson[o]]+sum[rson[o]];
}
 
int query(int o,int p,int l,int r,int k){
    if(l==r)return l;
    int m=l+r>>1,d=sum[lson[p]]-sum[lson[o]];
    if(d>=k)return query(lson[o],lson[p],l,m,k);else return query(rson[o],rson[p],m+1,r,k-d);
}
 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%s",str+1),insert(str,i);
    dfs(root);
    for(int i=1;i<=tot0;++i)if(!id[nod[i]].size())rt[i]=rt[i-1];else{
        update(rt[i],rt[i-1],1,n,id[nod[i]][0],1);
        for(int j=1;j<id[nod[i]].size();++j)update(rt[i],rt[i],1,n,id[nod[i]][j],1);
    }
    for(int i=1;i<=n;++i){
        scanf("%d",&k);
        if(sum[rt[lst[pos[i]]]]-sum[rt[dfn[pos[i]]-1]]<k){puts("-1");continue;}
        int x=query(rt[dfn[pos[i]]-1],rt[lst[pos[i]]],1,n,k);
        printf("%d\n",x);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值