【BZOJ3439】 Kpm的MC密码 (TRIE+主席树)
2016-08-24 10:59
323 查看
3439: 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
3cd
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
【题意】
给n(10^5)个字符串,总长(10^5),每个字符串给出ki。对于每个字符串si,把每个存在后缀为si的字符串拿出来,其中编号第ki小的就是si的答案。将每个字符串的答案输出。
【分析】
把字符串反过来建一颗字典树,那么它子树上的就都和和他同后缀。求出dfs序,问题就转化成区间第k小的数,用主席树解决。
代码如下:
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<queue> using namespace std; #define Maxn 100010 #define Maxd 20 struct node { int x,f,p,dfn,rh; int son[30]; }t[Maxn]; void upd(int x) { t[x].p=0; memset(t[x].son,0,sizeof(t[x].son)); } char s[Maxn]; int n,nt[Maxn],tot; int wh[Maxn],k[Maxn]; int mymax(int x,int y) {return x>y?x:y;} void init() { scanf("%d",&n); upd(0);tot=0; for(int i=1;i<=n;i++) { scanf("%s",s); int l=strlen(s); int now=0; for(int j=l-1;j>=0;j--) { int ind=s[j]-'a'+1; if(!t[now].son[ind]) { t[now].son[ind]=++tot; upd(tot);t[tot].f=now; } now=t[now].son[ind]; if(j==0) { nt[i]=t[now].p; t[now].p=i; wh[i]=now; } } } for(int i=1;i<=n;i++) scanf("%d",&k[i]); } int cnt,df[Maxn]; void dfs(int x) { t[x].dfn=t[x].rh=++cnt;df[cnt]=x; for(int i=1;i<=26;i++) if(t[x].son[i]) { dfs(t[x].son[i]); t[x].rh=t[t[x].son[i]].rh; } } int rt[Maxn],sum; struct hp { int son[2],cnt; }a[Maxn*Maxd]; void dfs2(int l,int x,int dep) { if(dep==0) return; if(a[x].son[0]) dfs2(a[l].son[0],a[x].son[0],dep-1); else a[x].son[0]=a[l].son[0]; if(a[x].son[1]) dfs2(a[l].son[1],a[x].son[1],dep-1); else a[x].son[1]=a[l].son[1]; } void build() { sum=0; a[0].son[0]=a[0].son[1]=a[0].cnt=0; rt[0]=0; for(int i=1;i<=tot;i++) { rt[i]=++sum; int l,r; l=rt[i-1],r=sum; a[rt[i]].cnt=a[rt[i-1]].cnt; for(int j=t[df[i]].p;j;j=nt[j]) { int x=j; a[rt[i]].cnt++; l=rt[i-1];r=rt[i]; for(int kk=17;kk>=1;kk--) { int ind=x/(1<<kk-1); x%=(1<<kk-1); l=a[l].son[ind]; if(!a[r].son[ind]) { a[r].son[ind]=++sum; a[sum].cnt=a[l].cnt; a[sum].son[0]=a[sum].son[1]=0; } r=a[r].son[ind]; a[r].cnt++; } } dfs2(rt[i-1],rt[i],17); } } int ffind(int l,int r,int kk) { l--; l=rt[l];r=rt[r]; int ans=0; if(a[r].cnt-a[l].cnt<kk) return -1; for(int i=17;i>=1;i--) { if(a[a[r].son[0]].cnt-a[a[l].son[0]].cnt>=kk) { l=a[l].son[0]; r=a[r].son[0]; } else { kk-=a[a[r].son[0]].cnt-a[a[l].son[0]].cnt; l=a[l].son[1]; r=a[r].son[1]; ans+=(1<<i-1); } } return ans; } int main() { init(); cnt=-1; dfs(0); build(); for(int i=1;i<=n;i++) { printf("%d\n",ffind(t[wh[i]].dfn,t[wh[i]].rh,k[i])); } return 0; }
[BZOJ3439]
2016-08-24 11:03:11
相关文章推荐
- BZOJ 3439: Kpm的MC密码( trie + DFS序 + 主席树 )
- 【BZOJ 3439】Kpm的MC密码 主席树+trie树
- 【BZOJ3439】Kpm的MC密码,trie树+dfs序+主席树
- BZOJ-3439:Kpm的MC密码(Trie+DFS序+主席树)
- BZOJ3439 Kpm的MC密码(可持久化trie)
- 【BZOJ 3439】Kpm的MC密码 主席树+trie树
- bzoj 3439: Kpm的MC密码 Trie+动态开点线段树
- bzoj 3439 Kpm的MC密码(Trie+dfs序+主席树)
- 【bzoj3439】Kpm的MC密码 trie树+主席树
- 3439: Kpm的MC密码 trie+主席树
- [BZOJ]3439: Kpm的MC密码 trie树+主席树(线段树合并)
- BZOJ 3439 Kpm的MC密码 Trie+可持久化线段树
- BZOJ-3439 Kpm的MC密码
- bzoj 3439: Kpm的MC密码
- [BZOJ3439]Kpm的MC密码
- bzoj 3439: Kpm的MC密码
- [BZOJ3439]Kpm的MC密码
- 【bzoj3439】KPM的MC密码
- BZOJ3439: Kpm的MC密码
- 【BZOJ】Kpm的MC密码-trie树+主席树+dfs序