【BZOJ-4212】神牛的养成计划 Trie树 + 可持久化Trie树
4212: 神牛的养成计划
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 136 Solved: 27
[Submit][Status][Discuss]
Description
Hzwer成功培育出神牛细胞,可最终培育出的生物体却让他大失所望......
后来,他从某同校女神 牛处知道,原来他培育的细胞发生了基因突变,原先决定神牛特征的基因序列都被破坏了,神牛hzwer很生气,但他知道基因突变的低频性,说不定还有以下优秀基因没有突变,那么他就可以用限制性核酸内切酶把它们切出来,然后再构建基因表达载体什么的,后面你懂的......
黄学长现在知道了N个细胞的DNA序列,它们是若干个由小写字母组成的字符串。一个优秀的基因是两个字符串s1和s2,当且仅当s1是某序列的前缀的同时,s2是这个序列的后缀时,hzwer认为这个序列拥有这个优秀基因。
现在黄学长知道了M个优秀基因s1和s2,它们想知道对于给定的优秀基因,有多少个细胞的DNA序列拥有它。
Input
第一行:N,表示序列数
接下来N行,每行一个字符串,代表N个DNA序列,它们的总长为L1
接下来一个M,表示询问数
接下来M行,每行两个字符串s1和s2,由一个空格隔开,hzwer希望你能在线回答询问,所以s1等于“s1”的所有字符按字母表的顺序向后移动ans位(字母表是一个环),ans为上一个询问的答案,s2同理。例如ans=2 “s1”=qz
则s1=sb。对于第一个询问,ans=0
s1和s2的总长度为L2
Output
输出M行,每行一个数,第i行的数表示有多少个序列拥有第i个优秀基因。
Sample Input
10
emikuqihgokuhsywlmqemihhpgijkxdukjfmlqlwrpzgwrwozkmlixyxniutssasrriafu
emikuqihgokuookbqaaoyiorpfdetaeduogebnolonaoehthfaypbeiutssasrriafu
emikuqihgokuorocifwwymkcyqevdtglszfzgycbgnpomvlzppwrigowekufjwiiaxniutssasrriafu
emikuqihgokuorociysgfkzpgnotajcfjctjqgjeeiheqrepbpakmlixyxniutssasrriafu
emikuqihgokuorociysgfrhulymdxsqirjrfbngwszuyibuixyxniutssasrriafu
emikuqihgokuorguowwiozcgjetmyokqdrqxzigohiutssasrriafu
emikuqihgokuorociysgsczejjmlbwhandxqwknutzgdmxtiutssasrriafu
emikuqihgokuorociysgvzfcdxdiwdztolopdnboxfvqzfzxtpecxcbrklvtyxniutssasrriafu
emikuqihgokuorocsbtlyuosppxuzkjafbhsayenxsdmkmlixyxniutssasrriafu
emikuqihgokuorociysgfjvaikktsixmhaasbvnsvmkntgmoygfxypktjxjdkliixyxniutssasrriafu
10
emikuqihgokuorociysg yxniutssasrriafu
aiegqmedckgqknky eqpoowonnewbq
xfbdnjbazhdnhkhvb qrqgbnmlltlkkbtyn
bjfhrnfedlhrlolzfv qppxpoofxcr
zhdfpldcbjf stsidponnvnmmdvap
zhdfpldcbjfpjmjxdt gdstsidponnvnmmdvap
dlhjtphgfnjtnqnbhxr wxwmhtsrrzrqqhzet
bjfhrnfedlhrlolzfv frqppxpoofxcr
zhdfpldcbjf dponnvnmmdvap
ucyakgyxweakehes nondykjiiqihhyqvk
emikuqihgokuhsywlmqemihhpgijkxdukjfmlqlwrpzgwrwozkmlixyxniutssasrriafu
emikuqihgokuookbqaaoyiorpfdetaeduogebnolonaoehthfaypbeiutssasrriafu
emikuqihgokuorocifwwymkcyqevdtglszfzgycbgnpomvlzppwrigowekufjwiiaxniutssasrriafu
emikuqihgokuorociysgfkzpgnotajcfjctjqgjeeiheqrepbpakmlixyxniutssasrriafu
emikuqihgokuorociysgfrhulymdxsqirjrfbngwszuyibuixyxniutssasrriafu
emikuqihgokuorguowwiozcgjetmyokqdrqxzigohiutssasrriafu
emikuqihgokuorociysgsczejjmlbwhandxqwknutzgdmxtiutssasrriafu
emikuqihgokuorociysgvzfcdxdiwdztolopdnboxfvqzfzxtpecxcbrklvtyxniutssasrriafu
emikuqihgokuorocsbtlyuosppxuzkjafbhsayenxsdmkmlixyxniutssasrriafu
emikuqihgokuorociysgfjvaikktsixmhaasbvnsvmkntgmoygfxypktjxjdkliixyxniutssasrriafu
10
emikuqihgokuorociysg yxniutssasrriafu
aiegqmedckgqknky eqpoowonnewbq
xfbdnjbazhdnhkhvb qrqgbnmlltlkkbtyn
bjfhrnfedlhrlolzfv qppxpoofxcr
zhdfpldcbjf stsidponnvnmmdvap
zhdfpldcbjfpjmjxdt gdstsidponnvnmmdvap
dlhjtphgfnjtnqnbhxr wxwmhtsrrzrqqhzet
bjfhrnfedlhrlolzfv frqppxpoofxcr
zhdfpldcbjf dponnvnmmdvap
ucyakgyxweakehes nondykjiiqihhyqvk
Sample Output
4
7
3
5
5
1
3
5
10
4
7
3
5
5
1
3
5
10
4
HINT
N<=2000
L1<=2000000
M<=100000
L2<=2000000
Source
Solution
首先对所有串按字典序排序,按字典序正序建Trie树,并在Trie树上记录节点区间。
然后查询一个前缀,在Trie树上跑到的节点所包含的区间即是可能成为答案的串区间,由于之前的排序,所以区间一定连续。
然后逆序建可持久化Trie树,对于前缀得到的区间LR,再利用可持久化Trie树跑后缀得到满足的子串树。
复杂度O(LenlogLen)排序暴力比较貌似有点智障,把垃圾排序去掉能做到$O(Len)$。
Code
#include<iostream> #include<cmath> #include<cstring> #include<algorithm> #include<map> #include<cstdio> using namespace std; #define MAXN 2000010 int N,M,id[MAXN],last,st[2010],ed[2010]; char S[MAXN],s1[MAXN],s2[MAXN]; inline bool cmp(int x,int y) { int st1=st[x],st2=st[y],ed1=ed[x],ed2=ed[y],l=min(ed1-st1,ed2-st2)+1; for (int i=1; i<=l; i++) { if (S[st1]==S[st2]) st1++,st2++; else return S[st1]<S[st2]; } return ed1-st1<ed2-st2; } namespace Trie{ int ls[MAXN],rs[MAXN],sz=1,son[MAXN][26]; inline void Insert(int x,int y) { int now=1; for (int i=st[x]; i<=ed[x]; i++) { int c=S[i]-'a'; if (son[now][c]) now=son[now][c]; else son[now][c]=++sz,now=son[now][c]; ls[now]=min(ls[now],y); rs[now]=max(rs[now],y); } } inline int Query() { int len=strlen(s1),now=1; for (int i=0; i<len; i++) { int c=s1[i]-'a'; if (!son[now][c]) return -1; now=son[now][c]; } return now; } inline void Clear() {memset(ls,63,sizeof(ls)); memset(rs,0,sizeof(rs));} } namespace PrTrie{ int root[MAXN+20010],sz,sum[MAXN+20010],son[MAXN+20010][26]; inline void Insert(int &x,int y,int k) { int now=x=++sz; for (int i=st[k]; i<=ed[k]; i++) { for (int j=0; j<26; j++) son[now][j]=son[y][j]; sum[now]=sum[y]+1; int c=S[i]-'a'; y=son[y][c],son[now][c]=++sz,now=son[now][c]; } sum[now]=sum[y]+1; } inline int Query(int L,int R) { int len=strlen(s2),re=0;; for (int i=0; i<len; i++) { int c=s2[i]-'a'; if (sum[son[R][c]]-sum[son[L][c]]) L=son[L][c],R=son[R][c]; else return 0; } return sum[R]-sum[L]; } } int main() { // freopen("godcow.in","r",stdin); // freopen("godcow.out","w",stdout); scanf("%d",&N); for (int i=1; i<=N; i++) { scanf("%s",S+ed[i-1]+1); id[i]=i; st[i]=ed[i-1]+1; ed[i]=st[i]+strlen(S+ed[i-1]+1)-1; } stable_sort(id+1,id+N+1,cmp); // for (int i=1; i<=N; i++) cout<<id[i]<<" "; cout<<endl; Trie::Clear(); for (int i=1; i<=N; i++) Trie::Insert(id[i],i); for (int i=1; i<=N; i++) reverse(S+st[i],S+ed[i]+1); for (int i=1; i<=N; i++) { using namespace PrTrie; Insert(root[i],root[i-1],id[i]); } scanf("%d",&M); while (M--) { scanf("%s%s",s1,s2); int l1=strlen(s1),l2=strlen(s2); for (int i=0; i<l1; i++) { int tmp=s1[i]-'a'; tmp+=last; tmp%=26; tmp+='a'; s1[i]=tmp; } for (int i=0; i<l2; i++) { int tmp=s2[i]-'a'; tmp+=last; tmp%=26; tmp+='a'; s2[i]=tmp; } reverse(s2,s2+l2); int pos=Trie::Query(),L,R; if (pos==-1) printf("%d\n",last=0); else { L=Trie::ls[pos],R=Trie::rs[pos]; // printf("%d %d\n",L,R); using namespace PrTrie; printf("%d\n",last=Query(root[L-1],root[R])); } } return 0; }
用cout输出一直无故RE...黑人问号...
——It's a lonely path. Don't make it any lonelier than it has to be.