【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
",find(c,w));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/rlddd/p/9785450.html