【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樹上記錄節點區間。php

而後查詢一個前綴,在Trie樹上跑到的節點所包含的區間便是可能成爲答案的串區間,因爲以前的排序,因此區間必定連續。ios

而後逆序建可持久化Trie樹,對於前綴獲得的區間LR,再利用可持久化Trie樹跑後綴獲得知足的子串樹。ui

複雜度O(LenlogLen)排序暴力比較貌似有點智障,把垃圾排序去掉能作到$O(Len)$。spa

Code

#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...黑人問號...blog

相關文章
相關標籤/搜索