zoukankan      html  css  js  c++  java
  • [BZOJ4212]神牛的养成计划

    [BZOJ4212]神牛的养成计划

    试题描述

    Hzwer 成功培育出神牛细胞,可最终培育出的生物体却让他大失所望......

    后来,他从某同校女神 牛处知道,原来他培育的细胞发生了基因突变,原先决定神牛特征的基因序列都被破坏了,神牛hzwer很生气,但他知道基因突变的低频性,说不定还有以下优秀基因没有突变,那么他就可以用限制性核酸内切酶把它们切出来,然后再构建基因表达载体什么的,后面你懂的......

    黄学长现在知道了 (N) 个细胞的 DNA 序列,它们是若干个由小写字母组成的字符串。一个优秀的基因是两个字符串 (s_1)(s_2),当且仅当 (s_1) 是某序列的前缀的同时,(s_2) 是这个序列的后缀时,hzwer 认为这个序列拥有这个优秀基因。

    现在黄学长知道了 (M) 个优秀基因 (s_1)(s_2),它们想知道对于给定的优秀基因,有多少个细胞的 DNA 序列拥有它。

    输入

    第一行:(N),表示序列数

    接下来 (N) 行,每行一个字符串,代表 (N) 个 DNA 序列,它们的总长为 (L_1)

    接下来一个 (M),表示询问数

    接下来 (M) 行,每行两个字符串 (s_1)(s_2),由一个空格隔开,hzwer 希望你能在线回答询问,所以 (s_1) 等于“(s_1)”的所有字符按字母表的顺序向后移动 (ans) 位(字母表是一个环),(ans) 为上一个询问的答案,(s_2) 同理。例如 (ans=2, “s_1”=qz)(s_1=sb)。对于第一个询问,(ans=0)(s_1)(s_2) 的总长度为 (L_2)

    输出

    输出有 (p) 行,YesNo.

    输入示例

    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
    

    数据规模及约定

    (N le 2000)

    (L_1 le 2000000)

    (M le 100000)

    (L_2 le 2000000)

    题解

    直接 (O(NM)) 哈希看上去复杂度是对的,但是大视野老爷机可能不是很资瓷……

    其实并不明白 (N) 为什么不给到和 (M) 同阶,反正复杂度都是 (O(L_1 + L_2)) 的。

    考虑建立 trie 树,这样直接解决了前缀的问题,我们可以发现所有合法的串一定都在 trie 树的某个子树中,而一个子树可以变成一段区间,那么我们就是要求一段区间内满足后缀是 (s_2) 的串有多少个。这样一来我们可以按照每个串在 trie 树中的 dfs 序,从小到大按反串建立可持久化 trie,这样对于一个区间直接就是两个版本的 trie 相减。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxl 2000010
    #define maxln 2100010
    #define maxa 26
    
    char S[maxl], S2[maxl];
    int str[maxl], str2[maxl];
    
    struct Trie {
    	int ToT, ch[maxl][maxa], fa[maxl], tot[maxl], clo, dl[maxl], dr[maxl], perm[maxl];
    	Trie() { ToT = 1; }
    	void Insert(char *S) {
    		int n = strlen(S), u = 1;
    		rep(i, 0, n - 1) {
    			int x = S[i] - 'a';
    			if(!ch[u][x]) fa[ch[u][x] = ++ToT] = u;
    			u = ch[u][x];
    		}
    		tot[u]++;
    		return ;
    	}
    	void build(int u) {
    		perm[dl[u] = ++clo] = u;
    		rep(c, 0, maxa - 1) if(ch[u][c]) build(ch[u][c]);
    		dr[u] = clo;
    		return ;
    	}
    } sol;
    
    struct RTrie {
    	int ToT, ch[maxln][maxa], rt[maxln], siz[maxln];
    	RTrie() { ToT = rt[0] = 1; }
    	void Insert(int id, char *S, int v) {
    		siz[++ToT] = siz[rt[id-1]] + v; memcpy(ch[ToT], ch[rt[id-1]], sizeof(ch[ToT])); rt[id] = ToT;
    		// printf("siz[%d] = %d (+ %d)
    ", ToT, siz[ToT], v);
    		int n = strlen(S), u = rt[id];
    		rep(i, 0, n - 1) {
    			int x = S[i] - 'a';
    			siz[++ToT] = siz[ch[u][x]] + v; memcpy(ch[ToT], ch[ch[u][x]], sizeof(ch[ToT])); ch[u][x] = ToT;
    			u = ch[u][x];
    		}
    		return ;
    	}
    	int query(int *s1, int *s2, int l1, int l2) {
    		int u = 1, vl, vr;
    		rep(i, 0, l1 - 1) u = sol.ch[u][s1[i]];
    		vl = rt[sol.dl[u]-1]; vr = rt[sol.dr[u]];
    		// printf("%d: [%d, %d] %d %d | %d %d
    ", u, sol.dl[u], sol.dr[u], vl, vr, siz[vl], siz[vr]);
    		dwn(i, l2 - 1, 0) vl = ch[vl][s2[i]], vr = ch[vr][s2[i]];
    		// printf("vl, vr: %d, %d %d %d
    ", vl, vr, siz[vl], siz[vr]);
    		return siz[vr] - siz[vl];
    	}
    } rsol;
    
    int main() {
    	int n = read();
    	rep(i, 1, n) scanf("%s", S), sol.Insert(S);
    	
    	sol.build(1);
    	rep(i, 1, sol.ToT) {
    		int u = sol.perm[i], l = 0;
    		if(!sol.tot[u]) {
    			rsol.rt[i] = rsol.rt[i-1];
    			continue;
    		}
    		int v = sol.tot[u];
    		while(u > 1) {
    			rep(c, 0, maxa - 1) if(sol.ch[sol.fa[u]][c] == u){ S[l++] = c + 'a'; break; }
    			u = sol.fa[u];
    		}
    		S[l] = 0;
    		// printf("rsol.Insert(%d, %s)
    ", i, S);
    		rsol.Insert(i, S, v);
    	}
    	
    	int q = read(), lstans = 0;
    	while(q--) {
    		scanf("%s%s", S, S2);
    		int l = strlen(S), l2 = strlen(S2);
    		rep(i, 0, l - 1) str[i] = (S[i] - 'a' + lstans) % maxa;
    		rep(i, 0, l2 - 1) str2[i] = (S2[i] - 'a' + lstans) % maxa;
    		printf("%d
    ", lstans = rsol.query(str, str2, l, l2));
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Docker swarm集群增加节点和删除节点
    docker 基础
    Mac 下 java环境 maven环境配置
    git 常用命令(含删除文件) git提交本地分支到远程分支
    github添加ssh keys
    Git在mac中和远程仓库建立连接
    初始化Git仓库(Mac环境下)
    Mac系统显示隐藏文件
    Mac:jenkins忘记管理员账号登录密码如何修改管理员账号
    java实现链表的反转
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8479408.html
Copyright © 2011-2022 走看看