BZOJ 4212: 神牛的养成计划
4212: 神牛的养成计划
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 142 Solved: 30
[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
万古神牛黄学长 Orz
又是一道可持久化Trie好题。
先想暴力,每次先找出有所能匹配上前缀的串,然后在看看这些串里有多少个还能匹配上后缀,这些串的个数就是最终答案。
然后发现,每次靠前缀筛出来的所有串一定具有一段相同的前缀,这个有点像后缀数组的那种感觉,就是先对所有串按照前缀排序,不难看出每次找出的前缀合法的串一定排成一个区间,我们想访问这个区间的所有串的逆序Trie,这个就是可持久化Trie树了。
从DaD3zZ那里看到这道题的,他说感觉对串按照前缀排序时使用了C++的Sort函数,逐位比较两个串的大小貌似比较暴力;但是不难想到,我们先建出正序的Trie后,在Trie上按顺序(a->z)DFS出来的就是按前缀排好序的了,这样复杂度就是完美的$O(\sum{Length})$,轻松过掉2000000。貌似出题人比较友好(可能就是比较懒),出的数据有点水……
代码没有,懒得写了…… 懒癌晚期
UPDATE 写了一发代码,然后图省事用的string,然后cin就给RE了,下午发现问题改成scanf就可以了。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int mxn = 2005; 6 const int mxm = 2100005; 7 8 inline void scan(string &s) 9 { 10 s.clear(); 11 12 static char buf[mxm]; 13 14 scanf("%s", buf); 15 16 for (char *c = buf; *c; ++c) 17 s.push_back(*c); 18 } 19 20 int n, m; 21 22 int ord[mxn]; 23 24 string str[mxn]; 25 26 int end[mxm]; 27 int son[mxm][26]; 28 29 int mini[mxm]; 30 int maxi[mxm]; 31 32 inline void insert(string &s, int id) { 33 static int tot = 1; 34 35 int p = 1, len = s.length(); 36 37 for (int i = 0; i < len; ++i) { 38 if (son[p][s[i] - 'a'] == 0) 39 son[p][s[i] - 'a'] = ++tot; 40 41 p = son[p][s[i] - 'a']; 42 } 43 44 end[p] = id; 45 } 46 47 void getOrder(int p) { 48 static int cnt; 49 50 mini[p] = cnt; 51 52 if (end[p]) 53 ord[++cnt] = end[p]; 54 55 for (int i = 0; i < 26; ++i) 56 if (son[p][i])getOrder(son[p][i]); 57 58 maxi[p] = cnt; 59 } 60 61 int root[mxn]; 62 63 int sum[mxm]; 64 int nxt[mxm][26]; 65 66 void insert(int &t, int p, string &s, int d) { 67 static int tot = 1; 68 69 t = ++tot; 70 71 sum[t] = sum[p] + 1; 72 73 memcpy(nxt[t], nxt[p], sizeof(nxt[t])); 74 75 if (d < s.length()) 76 insert(nxt[t][s[d] - 'a'], nxt[p][s[d] - 'a'], s, d + 1); 77 } 78 79 inline void trie1(string &s, int <, int &rt, int ans) { 80 int p = 1, len = s.length(); 81 82 for (int i = 0; i < len; ++i) 83 s[i] = (s[i] - 'a' + ans) % 26 + 'a'; 84 85 lt = rt = 0; 86 87 for (int i = 0; i < len; ++i) 88 p = son[p][s[i] - 'a']; 89 90 lt = mini[p]; 91 rt = maxi[p]; 92 } 93 94 inline void trie2(string &s, int lt, int rt, int &ans) { 95 int len = s.length(); 96 97 reverse(s.begin(), s.end()); 98 99 for (int i = 0; i < len; ++i) 100 s[i] = (s[i] - 'a' + ans) % 26 + 'a'; 101 102 int a = root[lt], b = root[rt]; 103 104 for (int i = 0; i < len; ++i) { 105 a = nxt[a][s[i] - 'a']; 106 b = nxt[b][s[i] - 'a']; 107 } 108 109 ans = sum[b] - sum[a]; 110 } 111 112 signed main(void) { 113 #ifndef ONLINE_JUDGE 114 freopen("in", "r", stdin); 115 freopen("out", "w", stdout); 116 #endif 117 118 scanf("%d", &n); 119 120 for (int i = 1; i <= n; ++i) 121 scan(str[i]); 122 123 for (int i = 1; i <= n; ++i) 124 insert(str[i], i); 125 126 getOrder(1); 127 128 for (int i = 1; i <= n; ++i) 129 reverse(str[i].begin(), str[i].end()); 130 131 for (int i = 1; i <= n; ++i) 132 insert(root[i], root[i - 1], str[ord[i]], 0); 133 134 scanf("%d", &m); 135 136 for (int i = 1, lt, rt, ans = 0; i <= m; ++i) { 137 static string s1, s2; 138 139 scan(s1); 140 scan(s2); 141 142 trie1(s1, lt, rt, ans); 143 trie2(s2, lt, rt, ans); 144 145 printf("%d\n", ans); 146 } 147 }
@Author: YouSiki