zoukankan      html  css  js  c++  java
  • 【bzoj4212】神牛的养成计划 Trie树+可持久化Trie树

    题目描述

    Hzwer成功培育出神牛细胞,可最终培育出的生物体却让他大失所望......
    后来,他从某同校女神 牛处知道,原来他培育的细胞发生了基因突变,原先决定神牛特征的基因序列都被破坏了,神牛hzwer很生气,但他知道基因突变的低频性,说不定还有以下优秀基因没有突变,那么他就可以用限制性核酸内切酶把它们切出来,然后再构建基因表达载体什么的,后面你懂的......
    黄学长现在知道了N个细胞的DNA序列,它们是若干个由小写字母组成的字符串。一个优秀的基因是两个字符串s1和s2,当且仅当s1是某序列的前缀的同时,s2是这个序列的后缀时,hzwer认为这个序列拥有这个优秀基因。
    现在黄学长知道了M个优秀基因s1和s2,它们想知道对于给定的优秀基因,有多少个细胞的DNA序列拥有它。

    输入

    第一行: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

    输出

    输出M行,每行一个数,第i行的数表示有多少个序列拥有第i个优秀基因。

    样例输入

    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

    样例输出

    4
    7
    3
    5
    5
    1
    3
    5
    10
    4


    题解

    Trie树+可持久化Trie树

    一开始都蛋疼的想到树套树了 = =

    先对所有字符串建立一棵Trie树。

    然后DFS一遍得到的Trie树,按照DFS序将字符串的反串插入到可持久化Trie树中,并记录以每个节点为根的子树中的字符串的范围。

    对于每个询问,将在Trie树中找s1,到达目标节点后可以直接拿出它包含字符串的范围,再将s2反过来,根据这个范围在可持久化Trie树中查找有多少个反串以它的反串为前缀(即以它为后缀),得到答案。

    注意判无解的情况。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 2000010
    using namespace std;
    int next[26][N] , fa[N] , tag[N] , lp[N] , rp[N] , tot = 1 , cnt , root[N] , c[26][N] , si[N] , num , len;
    char str[N] , ch[N] , ss[N] , ll;
    void insert(int x , int &y)
    {
    	int i , j , t = ++num;
    	y = t;
    	for(i = 0 ; i < len ; i ++ )
    	{
    		for(j = 0 ; j < 26 ; j ++ ) c[j][t] = c[j][x];
    		c[str[i] - 'a'][t] = ++num , x = c[str[i] - 'a'][x] , t = c[str[i] - 'a'][t] , si[t] = si[x] + 1;
    	}
    	for(i = 0 ; i < 26 ; i ++ ) c[i][t] = c[i][x];
    }
    int query(int x , int y)
    {
    	int i;
    	for(i = len - 1 ; ~i ; i -- ) x = c[str[i] - 'a'][x] , y = c[str[i] - 'a'][y];
    	return si[y] - si[x];
    }
    void dfs(int x)
    {
    	int i;
    	if(tag[x])
    	{
    		len = 0 , lp[x] = rp[x] = ++cnt;
    		for(i = x ; i != 1 ; i = fa[i]) str[len ++ ] = ch[i];
    		insert(root[cnt - 1] , root[cnt]);
    		return;
    	}
    	lp[x] = 1 << 30 , rp[x] = -1 << 30;
    	for(i = 0 ; i < 26 ; i ++ )
    		if(next[i][x])
    			dfs(next[i][x]) , lp[x] = min(lp[x] , lp[next[i][x]]) , rp[x] = max(rp[x] , rp[next[i][x]]);
    }
    void trans(char *s , int ans)
    {
    	int i , l = strlen(s);
    	for(i = 0 ; i < l ; i ++ ) s[i] = (s[i] - 'a' + ans) % 26 + 'a';
    }
    int main()
    {
    	int n , m , i , j , l , t , ans = 0;
    	scanf("%d" , &n);
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		scanf("%s" , str) , l = strlen(str);
    		for(j = 0 , t = 1 ; j < l ; j ++ )
    		{
    			if(!next[str[j] - 'a'][t]) next[str[j] - 'a'][t] = ++tot , ch[tot] = str[j] , fa[tot] = t;
    			t = next[str[j] - 'a'][t];
    		}
    		tag[t] = 1;
    	}
    	dfs(1);
    	scanf("%d" , &m);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		scanf("%s%s" , ss , str) , trans(ss , ans) , trans(str , ans) , l = strlen(ss) , len = strlen(str);
    		for(j = 0 , t = 1 ; t && j < l ; j ++ ) t = next[ss[j] - 'a'][t];
    		if(t) printf("%d
    " , ans = query(root[lp[t] - 1] , root[rp[t]]));
    		else printf("%d
    " , ans = 0);
    	}
    	return 0;
    }
    
  • 相关阅读:
    图片上传-下载-删除等图片管理的若干经验总结3-单一业务场景的完整解决方案
    图片上传-下载-删除等图片管理的若干经验总结2
    HDU 1195 Open the Lock
    HDU 1690 Bus System
    HDU 2647 Reward
    HDU 2680 Choose the best route
    HDU 1596 find the safest road
    POJ 1904 King's Quest
    CDOJ 889 Battle for Silver
    CDOJ 888 Absurdistan Roads
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7123559.html
Copyright © 2011-2022 走看看