SDOI2016 模式字符串

传送门
写法很显然,问题是怎么写。。
哈希是把长度为1~n的全部哈希出来,因为树的深度不超过 n n n嘛。
哈希出左到右和右到左两个方向。把模式串不断重复即可。然后就好写了。
注意一个去掉 k ∗ m k*m km的长度为 d d d的链,需要匹配的是长度为 m − d + 1 m-d+1 md+1的。因为当前的重心相当于是一个共用的字符。
不要 m e m s e t memset memset,还有 s i z siz siz小于 m m m的时候就不用搜下去了。

#include<bits/stdc++.h>
#define cs const
#define re register
#define ull unsigned long long
cs int N=1e6+10,M=1e6+10;
cs ull base=131;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch=gc();T x=0;
		while(!isdigit(ch)) ch=gc();
		while(isdigit(ch)) x=((x+(x<<2))<<1)+(ch^48),ch=gc();
		return x;
	}
	inline int gi(){return get<int>();}
	inline int ga(){
		char ch=gc();
		while(!isalpha(ch)) ch=gc();
		return ch-'A';
	}
}
using IO::gi;
using IO::ga;
inline void Max(int &a,int b){if(a<b)a=b;}
inline void Min(int &a,int b){if(a>b)a=b;}

int T,n,m,u,v,s[N],t[N];//s 匹配,t 树上节点。 
ull hash_lft[N],hash_rgt[N],Pow[N];int pre[N],suf[N];

int Head[N],Next[N<<1],V[N<<1],cnt=0;
int root,siz[N],vis[N],mx[N],SIZ;
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
inline void init(){
	memset(Head,0,sizeof Head),cnt=0;
	memset(vis,0,sizeof vis);
}

inline void getroot(int u,int f){
	mx[u]=0,siz[u]=1;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
		if((v!=f)&&(!vis[v])) getroot(v,u),Max(mx[u],siz[v]),siz[u]+=siz[v];
	Max(mx[u],SIZ-siz[u]);if(mx[u]<mx[root]) root=u;
}

int st_suf[N],top_suf=0;
int st_pre[N],top_pre=0;

inline int calc(int u,int f,int dep,ull state,int ret=0){
	int d=(dep-1)%m+1;
	if(state==hash_lft[dep]) ret+=suf[m-d+1],st_pre[++top_pre]=d;
	if(state==hash_rgt[dep]) ret+=pre[m-d+1],st_suf[++top_suf]=d;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
		if((v!=f)&&(!vis[v])) ret+=calc(v,u,dep+1,state*base+t[v]);
	return ret;
}

inline int getans(int u,int f,int ret=0,int len=0){
	pre[1]=(s[1]==t[u]),suf[1]=(s[m]==t[u]);
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]) if((v!=f)&&(!vis[v])){
		ret+=calc(v,u,2,base*t[u]+t[v]);
		while(top_pre) Max(len,st_pre[top_pre]),++pre[st_pre[top_pre--]];
		while(top_suf) Max(len,st_suf[top_suf]),++suf[st_suf[top_suf--]];
	}for(int re i=1;i<=len;++i) pre[i]=suf[i]=0;
	return ret;
}
inline int dfs(int u,int ret=0){
	vis[u]=1,ret+=getans(u,0);
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
		if(!vis[v]&&siz[v]>=m) SIZ=siz[v],root=0,getroot(v,u),ret+=dfs(root);
	return ret;
}
int main(){
//	freopen("2842.in","r",stdin);
	T=gi(),Pow[0]=1;
	for(int re i=1;i<N;++i) Pow[i]=Pow[i-1]*base;
	while(T--){
		init(),n=gi(),m=gi();
		for(int re i=1;i<=n;++i) t[i]=ga();
		for(int re i=1;i< n;++i) u=gi(),v=gi(),add(u,v),add(v,u);
		for(int re i=1;i<=m;++i) s[i]=ga();
		for(int re i=1;i<=n;++i) hash_lft[i]=hash_lft[i-1]+s[(i-1)%m+1]*Pow[i-1];
		for(int re i=1;i<=n;++i) hash_rgt[i]=hash_rgt[i-1]+s[m-(i-1)%m]*Pow[i-1];
		SIZ=n,mx[root=0]=n,getroot(1,0),printf("%d\n",dfs(root));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值