【bzoj3439】KPM的MC密码

这题乍一看后缀相等很烦的样子……

其实如果把字符串倒过来,那么相等的后缀就可以转化成前缀,前缀相等扔进trie就可以了。

剩下无非是Trie的树链kth,主席树随便维护就好。

注意一个串彻底结束才能打val++。而且因为主席树维护的权值是出现次数,连离散化都不用的。

#include<bits/stdc++.h>
#define N 500010
using namespace std;
int cnt,ch[N][26],val[N],in[N],out[N],tpos[N],n;
int sumv[N<<2],ls[N<<2],rs[N<<2];char s[N];
vector<int> q[N];
void ins(int x,char *s){
    int len=strlen(s);int now=0,u=0;
    for(int i=len-1;i>=0;i--,now=ch[now][u]){
        u=s[i]-'a';
        if(!ch[now][u])ch[now][u]=++cnt;
    }
    val[now]++;q[now].push_back(x);
}
void dfs(int x){
    for(int i=0;i<val[x];i++)in[q[x][i]]=cnt+1;
    for(int i=0;i<val[x];i++)tpos[++cnt]=q[x][i];
    for(int i=0;i<26;i++)if(ch[x][i])dfs(ch[x][i]);
    for(int i=0;i<val[x];i++)out[q[x][i]]=cnt;
}
void build(int x,int y,int l,int r,int q){
    sumv[x]=sumv[y]+1;
    if(l==r)return;int mid=(l+r)>>1;
    if(q<=mid){ls[x]=++cnt;rs[x]=rs[y];build(ls[x],ls[y],l,mid,q);}
    else{ls[x]=ls[y];rs[x]=++cnt;build(rs[x],rs[y],mid+1,r,q);}
}
int query(int l,int r,int x,int y,int v){
    if(l==r)return l;
    int mid=(l+r)>>1;int t=sumv[ls[y]]-sumv[ls[x]];
    if(t>=v)return query(l,mid,ls[x],ls[y],v);
    else return query(mid+1,r,rs[x],rs[y],v-t);
}
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)scanf("%s",s),ins(i,s);
    cnt=0;dfs(0);cnt=n+1;
    for(int i=1;i<=n;i++)build(i+1,i,1,n,tpos[i]);
    for(int i=1;i<=n;i++){
        int x=read();
        if(sumv[out[i]+1]-sumv[in[i]]<x)puts("-1");
        else printf("%d\n",query(1,n,in[i],out[i]+1,x));
    }
    return 0;
}

 

posted @ 2017-05-15 17:33  zcysky  阅读(279)  评论(0编辑  收藏  举报