1. 程式人生 > >BZOJ4212 神牛的養成計劃

BZOJ4212 神牛的養成計劃

題目大意

給定$n$個字串$S$,$m$次詢問,每次詢問給定兩個字串$s_1,s_2$,求$n$個字串中有多少個串滿足$s_1$是其字首且$s_2$是其後綴。

$n\leq 2000,m\leq 10^5 \sum |S|,\sum (|s_1|+|s_2|) \leq 2\times 10^6$

 

題解

考慮正反建$Trie$樹,原題就變為了在兩棵子樹中均出現的點有多少個。

你當然可以用主席樹加二維數點解決,其實也可以將每一個串按照正向$Trie$樹$Dfs$序排序,然後再反向建可持久化$Trie$,每次詢問就先找到正向$dfs$序要求的區間,接著在可持久化$Trie$的兩個對應的跟中沿著$s_2$的反串得出答案。

複雜度為$O(n\log n+\sum |S|+\sum (|s_1|+|s_2|)+m\log n)$。

所以其實$n$甚至可以出成$10^5$級。

 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 2220000
#define N 2010
using namespace std;
namespace IO{
	const int BS=(1<<19); int Top=0;
	char OT[BS],*OS=OT,SS[20]; const char *fin=OT+BS-1;
	void flush(){fwrite(OT,1,OS-OT,stdout);}
	void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;}
	void write(int x){
		if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-');
		while(x) SS[++Top]=x%10,x/=10;
		while(Top) Putchar(SS[Top]+'0'),--Top;
	}
	int read(){
		int nm=0,fh=1; char cw=getchar();
		for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
		for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
		return nm*fh;
	}
}using namespace IO;
char ch[M],*s[N],s1[M],s2[M]; int n,m,p[M][26],tg[N],len[N];
int sz[M],dfn[M],node[N],tot,Rt,rt[N];
int cnt,t[M][26],od[N],sum[M];
int ins(int &x,char *k,int rem){
	if(!x) x=++tot; if(!rem) return x;
	ins(t[x][k[0]-'a'],k+1,rem-1);
}
void dfs(int x){
	if(!x) return; dfn[x]=++cnt,sz[x]=1;
	for(int i=0;i<26;i++) dfs(t[x][i]),sz[x]+=sz[t[x][i]];
}
bool cmp(int x,int y){return dfn[node[x]]<dfn[node[y]];}
void ist(int &x,int pre,char *k,int rem){
	sum[x=++cnt]=sum[pre]+1;
	memcpy(p[x],p[pre],sizeof(p[x])); if(!rem) return;
	ist(p[x][k[0]-'a'],p[pre][k[0]-'a'],k-1,rem-1);
}
int qry(int x,int pre,char *k,int rem){
	if(sum[x]==sum[pre]) return 0; if(!rem) return sum[x]-sum[pre];
	return qry(p[x][k[0]-'a'],p[pre][k[0]-'a'],k-1,rem-1);
}
void match(int &l,int &r,int x,char *k,int rem){
	if(!rem){l=dfn[x],r=dfn[x]+sz[x]-1;return;}
	match(l,r,t[x][k[0]-'a'],k+1,rem-1);
}
int fd(int tim){
	int ls=1,rs=n,res=n+1,md;
	while(ls<=rs){
		md=((ls+rs)>>1);
		if(dfn[node[od[md]]]>tim) rs=md-1;
		else res=md,ls=md+1;
	} return res;
}
int main(){
	n=read(),s[0]=ch;
	for(int i=1;i<=n;i++){
		s[i]=s[i-1]+len[i-1],od[i]=i;
		scanf("%s",s[i]),len[i]=strlen(s[i]);
		node[i]=ins(Rt,s[i],len[i]);
	} dfs(Rt),cnt=0,sort(od+1,od+n+1,cmp);
	for(int i=1;i<=n;i++) ist(rt[i],rt[i-1],s[od[i]]+len[od[i]]-1,len[od[i]]);
	for(int T=read(),ans=0,len1,len2,L,R;T;T--,write(ans),Putchar('\n')){
		scanf("%s%s",s1,s2),len1=strlen(s1),len2=strlen(s2);
		for(int i=0;i<len1;i++) s1[i]=(s1[i]-'a'+ans)%26+'a';
		for(int i=0;i<len2;i++) s2[i]=(s2[i]-'a'+ans)%26+'a';
		match(L,R,Rt,s1,len1); if(!R){ans=0;continue;}
		L=fd(L-1),R=fd(R),ans=qry(rt[R],rt[L],s2+len2-1,len2);
	}flush();return 0;
}