【BZOJ4231】回忆树(AC自动机)(KMP)(DFS序)(树状数组)

传送门


题解:

一个串在从 u u u v v v的路径上匹配,我们直接分情况搞。

首先,匹配没有横跨LCA,那么一定是 L C A − > u LCA->u LCA>u的路径上匹配反串, L C A − > v LCA->v LCA>v匹配原串,直接用AC的fail树上DFS序离线上树状数组,一波就能搞了。

然后是横跨了LCA的,直接把前后提一个 ∣ S ∣ − 1 |S|-1 S1长度的串出来做匹配就行了。


代码(用了C++11):

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char get_char(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	inline char peek(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1;}
	inline char ga(){while(!isalpha(peek()))++p1;return *p1++;}
	
	inline int get_s(char *s){
		int len=0;char c;
		while(isspace(c=gc()));
		while(s[len++]=c,!isspace(c=gc())&&c!=EOF);
		s[len]='\0';return len;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second 

cs int N=1e5+7,M=3e5+7;

struct edge{int to,c;};
struct Query{int p,id;};

std::vector<edge> G[N];
std::vector<Query> qry[N];
inline void adde(int u,int v,int w){
	G[u].push_back((edge){v,w});
	G[v].push_back((edge){u,w});
}

inline void addQ(int u,int v,int w){
	qry[u].push_back((Query){v,w});
}

int d[N],fa[N],fs[N];
int siz[N],in[N],nd[N],top[N],son[N],dfn;

void dfs1(int u,int p){
	siz[u]=1;
	for(auto e:G[u])if(e.to!=p){
		fa[e.to]=u;d[e.to]=d[u]+1;fs[e.to]=e.c;
		dfs1(e.to,u);siz[u]+=siz[e.to];
		if(siz[e.to]>siz[son[u]])son[u]=e.to;
	}
}
void dfs2(int u,int tp){
	nd[in[u]=++dfn]=u;top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(auto e:G[u])if(e.to!=fa[u]&&e.to!=son[u])dfs2(e.to,e.to);
}
inline int LCA(int u,int v){
	while(top[u]!=top[v])d[top[u]]>d[top[v]]?u=fa[top[u]]:v=fa[top[v]];
	return d[u]<d[v]?u:v;
}
inline int Kth(int u,int k){
	while(d[u]-d[top[u]]<k)k-=d[u]-d[top[u]]+1,u=fa[top[u]];
	return nd[in[u]-k];
}

char s[M];int len;

namespace KMP{
	int nxt[M],a[N],tp;
	inline int solve_cross(int u,int v,int p){
		for(int re i=2,j=0;i<=len;++i){
			while(j&&s[j+1]!=s[i])j=nxt[j];
			if(s[j+1]==s[i])++j;nxt[i]=j;
		}tp=0;int re j=0,ans=0;
		u=Kth(u,d[u]-std::min(d[p]+len-1,d[u]));
		v=Kth(v,d[v]-std::min(d[p]+len-1,d[v]));
		for(;u!=p;u=fa[u]){
			while(j&&s[j+1]!=fs[u])j=nxt[j];
			if(s[j+1]==fs[u])++j;
		}
		for(;v!=p;v=fa[v])a[++tp]=fs[v];
		while(tp){
			while(j&&s[j+1]!=a[tp])j=nxt[j];
			if(s[j+1]==a[tp])++j;
			if(j==len)++ans,j=nxt[j];
			--tp;
		}
		return ans;
	}
}

namespace AC{
	cs int M=::M<<1;
	int cnt;
	int son[M][26],fail[M];
	std::vector<int> G[M];
	int in[M],out[M],dfn;
	
	inline int ins(){
		int u=0;
		for(int re i=1;i<=len;++i){
			int c=s[i];
			if(!son[u][c])son[u][c]=++cnt;
			u=son[u][c];
		}
		return u;
	}
	
	void dfs_fail(int u){
		in[u]=++dfn;
		for(int re v:G[u])dfs_fail(v);
		out[u]=dfn;
	}
	
	inline void build_AC(){
		std::queue<int> q;
		for(int re i=0;i<26;++i)if(son[0][i])q.push(son[0][i]);
		while(!q.empty()){
			int u=q.front();q.pop();
			for(int re i=0;i<26;++i)
			son[u][i]?(q.push(son[u][i]),fail[son[u][i]]=son[fail[u]][i]):(son[u][i]=son[fail[u]][i]);
		}
		for(int re i=cnt;i;--i)G[fail[i]].push_back(i);
		dfs_fail(0);
	}
	
	int tr[M];
	inline void add(int p,int v){for(p=in[p];p<=dfn;p+=p&-p)tr[p]+=v;}
	inline int query(int p){int res=0;for(;p;p^=p&-p)res+=tr[p];return res;}
	inline int qy(int u){return query(out[u])-query(in[u]-1);}
}

int ans[M];

void dfs3(int u,int ps){
	AC::add(ps,1);
	for(auto q:qry[u]){
		int t=q.id;
		if(t>0)ans[t]+=AC::qy(q.p);
		else ans[-t]-=AC::qy(q.p);
	}
	for(auto e:G[u])if(e.to!=fa[u])
	dfs3(e.to,AC::son[ps][e.c]);
	AC::add(ps,-1);
}

signed main(){
#ifdef zxyoi
	freopen("memory.in","r",stdin);
#endif
	int n=gi(),m=gi();
	for(int re i=1;i<n;++i){
		int u=gi(),v=gi();char c=ga();
		adde(u,v,c-'a');
	}dfs1(1,0);dfs2(1,1);
	for(int re i=1;i<=m;++i){
		int u=gi(),v=gi(),p=LCA(u,v);len=get_s(s+1);
		for(int re j=1;j<=len;++j)s[j]-='a';
		ans[i]=KMP::solve_cross(u,v,p);
		if(d[v]-d[p]>=len){
			int ps=AC::ins();
			addQ(v,ps,i);
			addQ(Kth(v,d[v]-d[p]-len+1),ps,-i);
		}
		if(d[u]-d[p]>=len){
			std::reverse(s+1,s+len+1);
			int ps=AC::ins();
			addQ(u,ps,i);
			addQ(Kth(u,d[u]-d[p]-len+1),ps,-i);
		}
	}
	AC::build_AC();dfs3(1,0);
	for(int re i=1;i<=m;++i)cout<<ans[i]<<"\n";
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值