3439: Kpm的MC密码 trie+主席树

一开始写的fail树,然后沿着fail指针反向dfs。结果TLE啦。
看了一下题解,是把后缀相同转化为前缀相同,然后在trie树上有相同前缀的肯定在同一棵子树中,按dfs序建主席树就行啦。
其实在fail树上建主席树也是可以的。。一开始傻逼没有想到。。懒得改了。。
注意有相同的子串!

fail树暴力

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define N 100005
using namespace std;
int n,cnt,top;
int a[N][26],p[N],pos[N],q[N],stack[N];
bool flag[N];
char s[N];
vector<int> b[N];
vector<int> id[N];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x)
{
    scanf("%s",s);
    int c,now=1,len=strlen(s);
    for (int i=0;i<len;i++)
    {
        c=s[i]-'a';
        if (a[now][c]) now=a[now][c];
        else now=a[now][c]=++cnt;

    }
    id[now].push_back(x); pos[x]=now; flag[now]=1;
}
inline void build_fail()
{
    int t=0,w=1,now;
    q[1]=1; p[1]=0;
    while (t<w)
    {
        now=q[++t];
        for (int i=0;i<26;i++)
            if (a[now][i])
            {
                int k=p[now];
                while (!a[k][i]) k=p[k];
                p[a[now][i]]=a[k][i];
                if (a[k][i]!=1) b[a[k][i]].push_back(a[now][i]);
                q[++w]=a[now][i];
            }
    }
}
void dfs(int x)
{
    if (flag[x]) 
        for (int i=0;i<id[x].size();i++) stack[++top]=id[x][i];
    for (int i=0;i<b[x].size();i++) dfs(b[x][i]);
}
int main()
{
//  freopen("password.in","r",stdin);
//  freopen("password.out","w",stdout);

    n=read(); cnt=1;
    for (int i=0;i<26;i++) a[0][i]=1;
    for (int i=1;i<=n;i++) insert(i);
    build_fail();
    for (int i=1;i<=n;i++) 
    {
        top=0;
        dfs(pos[i]);
        sort(stack+1,stack+top+1);
        int k=read();
        if (k>top) puts("-1");
        else printf("%d\n",stack[k]);
    }
    return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define N 100005
using namespace std;
int n,cnt,dfn,size;
int a[N][26];
int in[N],out[N],root[N],pos[N],p[N];
char s[N];
int ls[5000005],rs[5000005],sum[5000005];
vector<int> id[N];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x)
{
    scanf("%s",s);
    int c,now=1,len=strlen(s);
    for (int i=len-1;~i;i--)
    {
        c=s[i]-'a';
        if (a[now][c]) now=a[now][c];
        else now=a[now][c]=++cnt;
    }
    id[now].push_back(x); p[x]=now; 
}
void dfs(int x)
{
    in[x]=++dfn; pos[dfn]=x;
    for (int i=0;i<26;i++)
        if (a[x][i]) dfs(a[x][i]);
    out[x]=dfn;
}
void update(int l,int r,int x,int &y,int val)
{
    y=++size;
    ls[y]=ls[x]; rs[y]=rs[x]; sum[y]=sum[x]+1;
    if (l==r) return;
    int mid=l+r>>1;
    if (val<=mid) update(l,mid,ls[x],ls[y],val);
    else update(mid+1,r,rs[x],rs[y],val);
}
int query(int x,int y,int k)
{
    if (k>sum[y]-sum[x]) return -1;
    int l=1,r=n;
    while (l!=r)
    {
        int mid=l+r>>1;
        int tmp=sum[ls[y]]-sum[ls[x]];
        if (tmp>=k) r=mid,x=ls[x],y=ls[y];
        else l=mid+1,k-=tmp,x=rs[x],y=rs[y];
    }
    return l;
}
int main()
{
//  freopen("password.in","r",stdin);
//  freopen("password.out","w",stdout);

    n=read(); cnt=1;
    for (int i=0;i<26;i++) a[0][i]=1;
    for (int i=1;i<=n;i++) insert(i);
    dfs(1);
    for (int i=1;i<=cnt;i++)
        if (id[pos[i]].size()!=0) 
        {
            update(1,n,root[i-1],root[i],id[pos[i]][0]);
            for (int j=1;j<id[pos[i]].size();j++) 
                update(1,n,root[i],root[i],id[pos[i]][j]);
        }
        else root[i]=root[i-1];
    for (int i=1;i<=n;i++)
    {
        int k=read();
        printf("%d\n",query(root[in[p[i]]-1],root[out[p[i]]],k));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值