题目大意
信息是二进制的,共有M条。
约翰已经拦截了这些信息,知道了第i条二进制信息的前b_i位。
他同时知道,奶牛使用N条密码。
但是,他仅了解第j条密码的前c_j位。
对于每条密码j,他想知道有多少截得的信息能够和它匹配。
也就是说,有多少信息和这条密码有着相同的前缀。
样例解释
4条信息,5条密码
截获的信息前缀是010,1,100,110。
可能的密码前缀是0,1,01,01001,11。
0只配对010;
1配对1,100,110;
01只配对010;
01001配对010;
11配对1,110。
题目分析
根据题意:answer=该密码作为别的信息前缀+别的信息作为该密码前缀的情况。
让我们分类讨论一下(滑稽)
1、该密码作为前缀
加入一个变量tr[i].s记录经过该结点的单词数量。
即从根节点到这个结点路径一模一样。
即有多少个单词的前缀一样。
那么该密码跑到最后的tr[i].s即为该密码作为别的信息前缀的情况。
2、别的信息作为该密码前缀
即跑的时候注意经过所有结点的end相加。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,len=0;
struct node{int son[10],end,s;}tr[1000010];
void bt(int l)
{
int now=0;
for(int i=1;i<=l;i++)
{
int x; scanf("%d",&x);
if(tr[now].son[x])
{
now=tr[now].son[x];
tr[now].s++;
}
else
{
len++; tr[now].son[x]=len;
now=len; tr[now].end=0;
tr[now].s++;
tr[now].son[0]=tr[now].son[1]=0;
}
}
tr[now].end++;
}
int find(int l)
{
bool gg=false;
int now=0,s=0;
for(int i=1;i<=l;i++)
{
int x; scanf("%d",&x);
if(gg) continue;
if(tr[now].son[x])
{
now=tr[now].son[x];
s+=tr[now].end;//信息作为该密码的前缀
}
else gg=true;
//没有人跟他配了找下去也没有意义啊
}
if(gg) return s;
return s+(tr[now].s-tr[now].end);
//答案还要加上该密码作为别的信息前缀的情况
//如果该密码与别的信息相同,已经作为前缀算过一遍了
//所以要 -tr[now].end
//tr[now].s记录 经过该结点的单词数量 即从根节点到这个结点路径一模一样
//即有多少个单词的前缀一样
}
int main()
{
tr[0].son[0]=tr[0].son[1]=0;
scanf("%d%d",&n,&m);
while(n--)
{
int k; scanf("%d",&k);
bt(k);
}
while(m--)
{
int k; scanf("%d",&k);
printf("%d\n",find(k));
}
return 0;
}