【BZOJ4212】神牛的养成计划
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
题解:对于前缀的要求,我们可以直接构建一棵Trie树,但是后缀的要求比较难搞。这就需要我们知道Trie树上的每个点被哪些单词所包含,然后判断这些单词的后缀是否满足要求。
这里又用到了DFS序的性质,当我们给所有单词按字典序排序后,Trie树上每个节点的子树里含有的单词 都在一段连续的区间中(Trie树的DFS序上的区间)。我们可以先利用Trie树将所有单词排序,然后求出每个节点对应的区间,再按照DFS序,对每个单词的反串维护一棵可持久化Trie树。这样,我们在查询的时候,只需要将s1在Trie树上跑一边,得到那个点对应的区间,再从可持久化Trie树里将那个区间拿出来,让s2在那里跑一边,看一下最终节点的siz就行了。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxl=2000010; int n,m,ans; int rank,tot1,tot2; int rt[2010],lp[2010],rp[2010]; char str[maxl],s1[maxl],s2[maxl]; struct trie { int ch[26],org,ls,rs; }t1[maxl]; struct ktrie { int ch[26],siz; }t2[maxl]; void insert(int x,int y,int z) { rt[y]=++tot2,y=rt[y]; int i,j,a; for(i=rp[z]-1;i>=lp[z];i--) { a=str[i]-'a',t2[y].ch[a]=++tot2; for(j=0;j<26;j++) if(j!=a) t2[y].ch[j]=t2[x].ch[j]; y=t2[y].ch[a],x=t2[x].ch[a],t2[y].siz=t2[x].siz+1; } } int query(int x,int y) { int i,a=strlen(s2),b; for(i=a-1;i>=0;i--) b=s2[i]-'a',y=t2[y].ch[b],x=t2[x].ch[b]; return t2[y].siz-t2[x].siz; } void dfs(int x) { t1[x].ls=rank+1; if(!x) return ; if(t1[x].org) t1[x].ls=t1[x].rs=++rank,insert(rt[rank-1],rank,t1[x].org); for(int i=0;i<26;i++) dfs(t1[x].ch[i]); t1[x].rs=rank; } int main() { scanf("%d",&n); int i,j,a,b,u; tot1=1; for(i=1;i<=n;i++) { lp[i]=rp[i-1],scanf("%s",str+lp[i]),rp[i]=strlen(str); for(u=1,j=lp[i];j<rp[i];j++) { b=str[j]-'a'; if(!t1[u].ch[b]) t1[u].ch[b]=++tot1; u=t1[u].ch[b]; } t1[u].org=i; } t1[0].ls=1<<30,t1[0].rs=0,dfs(1); scanf("%d",&m); for(i=1;i<=m;i++) { scanf("%s%s",s1,s2),a=strlen(s1),b=strlen(s2),u=1; for(j=0;j<a;j++) s1[j]=(s1[j]-'a'+ans)%26+'a'; for(j=0;j<b;j++) s2[j]=(s2[j]-'a'+ans)%26+'a'; for(j=0;j<a;j++) u=t1[u].ch[s1[j]-'a']; if(!u) ans=0; else ans=query(rt[t1[u].ls-1],rt[t1[u].rs]); printf("%d ",ans); } return 0; }