• 【BZOJ3439】Kpm的MC密码 Trie树+可持久化线段树


    【BZOJ3439】Kpm的MC密码

    Description

     背景

        想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的。。。),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了。。。

     描述

        Kpm当年设下的问题是这样的:

        现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串。

        系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每一个ki,要求你求出列出的n个字符串中所有是si的kpm串的字符串的编号中第ki小的数,如果不存在第ki小的数,则用-1代替。(比如说给出的字符串是cd,abcd,bcd,此时k1=2,那么”cd”的kpm串有”cd”,”abcd”,”bcd”,编号分别为1,2,3其中第2小的编号就是2)(PS:如果你能在相当快的时间里回答完所有n个ki的查询,那么你就可以成功帮kpm进入MC啦~~)

    Input

        第一行一个整数 n 表示字符串的数目

        接下来第二行到n+1行总共n行,每行包括一个字符串,第i+1行的字符串表示编号为i的字符串

        接下来包括n行,每行包括一个整数ki,意义如上题所示

    Output

        包括n行,第i行包括一个整数,表示所有是si的kpm串的字符串的编号中第ki小的数

    Sample Input

    3
    cd
    abcd
    bcd
    2
    3
    1

    Sample Output

    2
    -1
    2
    样例解释
    “cd”的kpm 串有”cd”,”abcd”,”bcd”,编号为1,2,3,第2小的编号是
    2,”abcd”的kpm串只有一个,所以第3小的编号不存在,”bcd”的kpm
    串有”abcd”,”bcd”,第1小的编号就是2。
    数据范围与约定
    设所有字符串的总长度为len
    对于100%的数据,1<=n<=100000,0<=len<=300000

    题解:写完可持久化Trie树和各种主席树这道题就显得很水了~

    将单词倒序扔到Trie树中,然后根据Trie树中的父子关系在重新构建一棵树。这样就满足新树中一个节点的子树中的所有节点都是该节点的kpm串,于是直接用主席树维护DFS序即可。

    结果交上去秒WA,原因是可能有重复的字符串!!所以要把它们合成一个串考虑,用vector存一下就好了(当然,我依然是用的链表来存)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    struct node
    {
    	int ch[26],from,org;
    }p[300010];
    struct sag
    {
    	int ls,rs,siz;
    }s[2000010];
    int n,m,cnt,sum,tot;
    int pos[100010],to[100010],next[100010],head[100010],q1[100010],q2[100010],rt[100010],fa[100010];
    int pre[100010];
    char str[3000010];
    void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    void insert(int x,int &y,int l,int r,int pos)
    {
    	if(pos>r)	return ;
    	y=++tot;
    	if(l==r)
    	{
    		s[y].siz=1;
    		return ;
    	}
    	int mid=l+r>>1;
    	if(pos<=mid)	s[y].rs=s[x].rs,insert(s[x].ls,s[y].ls,l,mid,pos);
    	else	s[y].ls=s[x].ls,insert(s[x].rs,s[y].rs,mid+1,r,pos);
    	s[y].siz=s[s[y].ls].siz+s[s[y].rs].siz;
    }
    int query(int x,int y,int l,int r,int k)
    {
    	if(l==r)	return l;
    	int mid=l+r>>1,sm=s[s[y].ls].siz-s[s[x].ls].siz;
    	if(sm>=k)	return query(s[x].ls,s[y].ls,l,mid,k);
    	return query(s[x].rs,s[y].rs,mid+1,r,k-sm);
    }
    void dfs(int x)
    {
    	q1[x]=q1[0];
    	int i;
    	for(i=x;i;i=pre[i])	insert(rt[q1[0]],rt[q1[0]+1],1,n,i),q1[0]++;
    	for(i=head[x];i!=-1;i=next[i])	dfs(to[i]);
    	q2[x]=q1[0];
    	for(i=pre[x];i;i=pre[i])	q1[i]=q1[x],q2[i]=q2[x];
    }
    int main()
    {
    	scanf("%d",&n);
    	int i,j,a,b,u;
    	sum=1;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<=n;i++)
    	{
    		scanf("%s",str),a=strlen(str);
    		u=1;
    		for(j=a-1;j>=0;j--)
    		{
    			b=str[j]-'a';
    			if(!p[u].ch[b])	p[u].ch[b]=++sum,p[p[u].ch[b]].from=u;
    			u=p[u].ch[b];
    		}
    		pre[i]=p[u].org,p[u].org=i,pos[i]=u;
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(p[pos[i]].org!=i)	continue;
    		for(j=p[pos[i]].from;j!=1&&!p[j].org;j=p[j].from);
    		fa[i]=p[j].org,add(fa[i],i);
    	}
    	dfs(0);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a);
    		if(q2[i]-q1[i]<a)	printf("-1
    ");
    		else	printf("%d
    ",query(rt[q1[i]],rt[q2[i]],1,n,a));
    	}
    	return 0;
    }

     

  • 相关阅读:
    Laravel Providers——服务提供者的注册与启动源码解析
    基于visual Studio2013解决算法导论之054图的邻接矩阵表示
    基于visual Studio2013解决算法导论之053图的邻接表表示
    基于visual Studio2013解决算法导论之052深度优先
    基于visual Studio2013解决算法导论之051区间树
    基于visual Studio2013解决算法导论之050强连通分支
    基于visual Studio2013解决算法导论之049活动选择问题
    基于visual Studio2013解决算法导论之048红黑树
    基于visual Studio2013解决算法导论之047赫夫曼编码
    基于visual Studio2013解决算法导论之046广度优先搜索
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/6895890.html
Copyright © 2020-2023  润新知