[Usaco2008 Dec]Secret Message 秘密信息

题意

贝茜正在领导奶牛们逃跑.为了联络,奶牛们互相发送秘密信息.

信息是二进制的,共有M(1≤M≤50000)条.反间谍能力很强的约翰已经部分拦截了这些信息,知道了第i条二进制信息的前bi(l《bi≤10000)位.他同时知道,奶牛使用N(1≤N≤50000)条密码.但是,他仅仅了解第J条密码的前cj(1≤cj≤10000)位.

对于每条密码J,他想知道有多少截得的信息能够和它匹配.也就是说,有多少信息和这条密码有着相同的前缀.当然,这个前缀长度必须等于密码和那条信息长度的较小者.

在输入文件中,位的总数(即∑Bi+∑Ci)不会超过500000.

分析

参照jklover的题解。

将信息串插入 Trie 树中,记录子树大小.询问时,是密码串前缀的,在搜索过程中会经过,密码串是它的前缀的,到最后一个字符处加上子树大小即可.

时间复杂度:线性。

代码

实现起来有个细节,根节点必须是1。因为如果按密码串匹配中途失败之后,就不应该继续匹配,而是一直停留在一个无效节点。所以把根节点设成1,然后无效节点自动为0,解决了这个技术问题。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
	rg T data=0,w=1;
	rg char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		data=data*10+ch-'0';
		ch=getchar();
	}
	return data*w;
}
template<class T>il T read(rg T&x)
{
	return x=read<T>();
}
typedef long long ll;
using namespace std;
co int N=5e5+1;
int tot=1,ch[N][2],val[N],siz[N];
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int n=read<int>(),m=read<int>();
	while(n--)
	{
		int u=1,l=read<int>(); // edit 1:root must be 1
		while(l--)
		{
			int b=read<int>();
			if(!ch[u][b]) ch[u][b]=++tot;
			u=ch[u][b],++siz[u];
		}
		++val[u],--siz[u];
	}
	while(m--)
	{
		int u=1,l=read<int>(),ans=0;
		while(l--)
		{
			int b=read<int>();
			u=ch[u][b];
			ans+=val[u];
		}
		ans+=siz[u];
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/10331516.html