【bzoj1590】【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.
输入
第1行输入N和M,之后N行描述秘密信息,之后M行描述密码.每行先输入一个整数表示信息或密码的长度,之后输入这个信息或密码.所有数字之间都用空格隔开.
输出
共M行,输出每条密码的匹配信息数.
样例输入
4 5 3 0 1 0 1 1 3 1 0 0 3 1 1 0 1 0 1 1 2 0 1 5 0 1 0 0 1 2 1 1
样例输出
1 3 1 1 2
题解
先把信息建成字典树。每个信息的结尾打上标记,并记录以该节点结尾的有多少个。查询的时候,如果是前缀等于信息的情况,那么就返回在字典树上经过的标记的数目,如果是前缀等于密码的情况,那么返回密码的最后一个字符的子树中的标记数。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=50000*10; int t[maxn][2],n,m,len,root,tot,cnt[maxn],num[maxn]; char c[500000+50]; bool word[maxn]; void insert(char *s,int r){ root=0;int id; for(int i=0;i<r;i++){ id=s[i]-'0'; if(!t[root][id]) t[root][id]=++tot; cnt[root]++;root=t[root][id]; } word[root]=true;num[root]++; } int find(char *s,int r){ root=0;int id,ans=0; for(int i=0;i<r;i++){ id=s[i]-'0'; if(!t[root][id]){ return ans; } root=t[root][id];if(word[root]) ans+=num[root]; } return ans+cnt[root]; } template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int main(){ read(m),read(n); for(int i=1;i<=m;i++){ int x,w; read(w);for(int i=1;i<=w;i++) read(x),c[i-1]=x+'0'; insert(c,w); } for(int i=1;i<=n;i++){ int x,w; read(w);for(int i=1;i<=w;i++) read(x),c[i-1]=x+'0'; printf("%d\n",find(c,w)); } return 0; }