BZOJ 4212: 神牛的养成计划

4212: 神牛的养成计划

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 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

Sample Output

4
7
3
5
5
1
3
5
10
4

HINT

 

N<=2000

L1<=2000000

M<=100000

L2<=2000000

 

 

Source

 
[Submit][Status][Discuss]

 

万古神牛黄学长 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 &lt, 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

posted @ 2017-03-14 20:25 YouSiki 阅读(...) 评论(...) 编辑 收藏