字典树(trie树) luoguP2922

题目描述

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

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

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

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

输入输出格式
输入格式:
*第1行:两个整数:M和N.

*行2..M + 1:行i + 1描述截取的代码i,其中包含整数b_i,后跟b_i空格分隔的0和1

*行M + 2..M + N + 1:行M + j + 1描述具有整数c_j的代码字j,后跟c_j空格分隔的0和1

输出格式:
*行1..M:行j:第j个代码字可以匹配的消息数。

 这道题用暴力并不好解决,而且数据量大,更是不可能。
 下面介绍一个神奇的东西----trie树
字典树(trie树) luoguP2922_第1张图片
image.png

trie树将信息存于边,以点相连,构成一棵树,如图所示;
从而判断前缀时可以在原先建成的树从树根开始搜;

对于该题,因为要统计前缀的数目,需要考虑该串是别的串的前缀的情况,还需要考虑别的串是该串的前缀的情况。 所以在建树的时候需要记录该节点的子树上有多少个数串的结尾,便于统计询问的串是别的串的前缀的情况;同时在记录该节点是多少个数串的结尾,以便于统记别的串是询问的串的前缀的情况;
那么就可以完美的完成这一题了;

#include
#include
#include
#include
#include
#include
#define INF 9999999
using namespace std;
struct Node{
    int son[3],path,end;
}N[1000010];
int n,m,bi,cj,cnt=1,a[10010];
void insert(int a[]){
    int now=1;
    for(int i=1;i<=bi;i++){
        if(N[now].son[a[i]]){N[now].path++; now=N[now].son[a[i]];}
        else{N[now].path++; N[now].son[a[i]]=++cnt; now=cnt;} //子树结尾数加一
    }
    N[now].end++;//结尾数加一
}
int count(int a[]){
    int now=1,sum=0;
    for(int i=1;i<=cj;i++){
        if(!N[now].son[a[i]]) return sum;
        else{
            sum+=N[N[now].son[a[i]]].end;
            now=N[now].son[a[i]];
        }
    }
    return sum+N[now].path;
}
int main(){
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++){
        scanf("%d",&bi);
        for(int j=1;j<=bi;j++) scanf("%d",&a[j]);
        insert(a); 
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&cj);
        for(int j=1;j<=cj;j++) scanf("%d",&a[j]);
        int ans=count(a); 
        printf("%d\n",ans);
    }
    return 0;
}

点个关注给个赞谢谢啦!!

你可能感兴趣的:(字典树(trie树) luoguP2922)