Processing math: 100%

【BZOJ-4212】神牛的养成计划 Trie树 + 可持久化Trie树

4212: 神牛的养成计划

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

Sample Output

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#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...黑人问号...

posted @   DaD3zZ  阅读(371)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥
点击右上角即可分享
微信分享提示