【bzoj3439】kpm的mc密码 题解

题目大意:

有n个字符串,编号为1~n,求每一个字符串在其他字符串中以它为后缀的字符串中编号第k小的字符串的编号。

思路:

将字符串倒过来建Trie,记录每个结尾节点的编号(可能会有重复,所以开一个vector记录)。再对trie树进行dfs序,记录结尾节点的子树区间。区间第k小,自然用可持久化线段树(由于权值就是id,所以不用离散化)。 注意:dfs序在遇到结尾节点时才++记录序号的变量cnt,而且相同串的dfs序号是一样的(就是说一个结尾节点可能包含好多个不同编号的串),左区间取最前的一个(比如dfs到x节点,编号为2,x节点存着两个串,那么它们的左区间都为2,而序号要加两次)

参考自:http://blog.csdn.net/xym_CSDN/article/details/51340321

代码:

 1 #include<vector>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<iostream>
 5 #define M 500009
 6 using namespace std;
 7 vector <int> q[M];
 8 int cnt,trie[M][26],num[M],com[M],out[M],dat[M],sum[M<<2],lc[M<<2],rc[M<<2];
 9 char s[M];
10
11 void ins(int id,char *str)
12 {
13     int i,l=strlen(str),j=0,k;
14     for (i=l-1;i>=0;i--,j=trie[j][k])
15         if (!trie[j][k=str[i]-‘a‘]) trie[j][k]=++cnt;
16     num[j]++; q[j].push_back(id);
17 }
18
19 void dfs(int x)
20 {
21     int i;
22     for (i=0;i<num[x];i++) com[q[x][i]]=cnt+1;
23     for (i=0;i<num[x];i++) dat[++cnt]=q[x][i];
24     for (i=0;i<26;i++) if (trie[x][i]) dfs(trie[x][i]);
25     for (i=0;i<num[x];i++) out[q[x][i]]=cnt;
26 }
27
28 void build(int l,int r,int cur,int _cur,int x)
29 {
30     sum[cur]=sum[_cur]+1;
31     if (l==r) return; int mid=l+r>>1;
32     if (x<=mid) lc[cur]=++cnt,rc[cur]=rc[_cur],build(l,mid,lc[cur],lc[_cur],x);
33     else lc[cur]=lc[_cur],rc[cur]=++cnt,build(mid+1,r,rc[cur],rc[_cur],x);
34 }
35
36 int ask(int L,int R,int l,int r,int k)
37 {
38     if (L==R) return L;
39     int mid=L+R>>1,t=sum[lc[r]]-sum[lc[l]];
40     if (t>=k) return ask(L,mid,lc[l],lc[r],k);
41     else return ask(mid+1,R,rc[l],rc[r],k-t);
42 }
43
44 int main()
45 {
46     int n,i,x; scanf("%d",&n);
47     for (i=1;i<=n;i++) scanf("%s",s),ins(i,s);
48     for (cnt=0,dfs(0),cnt=n+1,i=1;i<=n;i++) build(1,n,i+1,i,dat[i]);
49     for (i=1;i<=n;i++)
50     {
51         scanf("%d",&x);
52         if (sum[out[i]+1]-sum[com[i]]<x) printf("-1\n");
53         else printf("%d\n",ask(1,n,com[i],out[i]+1,x));
54     }
55     return 0;
56 }
时间: 2024-11-05 18:39:32

【bzoj3439】kpm的mc密码 题解的相关文章

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

【BZOJ3439】 Kpm的MC密码 (TRIE+主席树)

3439: Kpm的MC密码 Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了... 描述 Kpm当年设下的问题是这样的: 现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串. 系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分

【BZOJ】【3439】Kpm的MC密码

Trie树/可持久化线段树 神题啊……搞了我一下午= =(其实第233个提交也是我的) 我一开始的思路:这个找kpm串的过程,其实就跟在AC自动机上沿fail倒着往下走是差不多的(看当前是哪些点的后缀,如果某个串的后缀是当前串,那它的fail就会指向这里)所以就在fail树上bfs一遍,然后找到所有的编号,排序后输出第k小…… 然后顺利地WA了 QAQ 只好取膜拜题解……哦原来只要把每个串倒过来,建Trie树的时候,就相当于找当前这个串的子树! 在子树中统计啊……so easy啦-dfs一遍,把

BZOJ 3439: Kpm的MC密码( trie + DFS序 + 主席树 )

把串倒过来插进trie上, 那么一个串的kpm串就是在以这个串最后一个为根的子树, 子树k大值的经典问题用dfs序+可持久化线段树就可以O(NlogN)解决 ------------------------------------------------------------------ #include<cstdio> #include<cstring> #include<algorithm> #include<cctype> using namespa

【BZOJ3439】Kpm的MC密码 trie树+主席树

Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了... 描述 Kpm当年设下的问题是这样的: 现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串. 系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每 一个k

BZOJ 3439 Kpm的MC密码 Trie+可持久化线段树

题目大意:定义一种串,如果一个串是另一个串的后缀,那么这个串称作kpm串.问一个串的标号第k大的kpm串是多少. 思路:将所有的串翻转之后变成前缀,全都插进一个Trie树中.每个节点维护一个last指针,表示最后一次更新的可持久化线段树的指针,如果再有串经过这里,就继续更新last指针.最后只需要查询last指针中的东西就可以了. CODE: #include <cstdio> #include <cstring> #include <iostream> #includ

BZOJ 3439 Kpm的MC密码

倒着建trie,然后主席树来求子树第k大. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define maxn 300500 using namespace std; int n,trie[maxn][27],fath[maxn],cnt=0,tag[maxn],k,rot,kr,dfn[maxn],mx[m

BZOJ 3439 Kpm的MC密码 Trie树+可持久化线段树

题目大意:给定n个字符串,对于每个字符串求以这个字符串为后缀的字符串中第k小的编号 首先将字符串反转 那么就变成了对于每个字符串求以这个字符串为前缀的字符串中第k小的编号 然后考虑对字符串排序 那么对于每个字符串以它为前缀的字符串一定是连续的 那么就转化成了区间第k小 这个用可持久化线段树可以解决 排序自然不能直接排 既然是字符串 考虑Trie树+DFS即可 注意字符串有重复的 小心 #include <vector> #include <cstdio> #include <

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种