[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\n",ans);
}
return 0;
}
静渊以有谋,疏通而知事。