洛谷 P2922 [USACO08DEC]秘密消息Secret Message

目录:


题目:

传送门


分析:

首先是读入。这道题全部采用 2 2 进制,也就是说只有0 1 1 两个字符/数字,那么我们直接把它们当成字符用好了。
然后建树,也是一般的字典树建树法,只是把字母换成了0 1 1 而已,这里不多讲。
一边建树,一边在这串数列中走过的路径中的sum+1 sum s u m 代表有 sum s u m 个单词经过这个节点。
当一个单词插完以后,在当前节点(即该单词的最后一个数字)的 end+1 e n d + 1 end e n d 代表有 end e n d 个单词在这个节点终结。
注意可能会有重复的信息,也就是说可能会有多条信息在同一个节点终结,所以这里的 end e n d 是数字而不是布尔型。
然后读入待查询的信息,就像普通字典树查询一样地往下走,一边走一边把沿途的 end e n d 值加起来。
循环结束,有两种情况:一是该信息全部走完,二是再往下走没有与该信息相符的节点了。
第一种情况,当前的答案要减去当前节点的 end e n d 值再加上当前节点的 sum s u m 值(想一想,为什么)。
第二种情况,直接输出答案。
显然比待查询的信息长的信息,如果与待查询信息有相同前缀的话,一定会经过待查询信息终结的节点。
如果待查询信息无法终结,说明没有比该信息长且前缀是该信息的信息,所以不能加上 sum s u m
如果信息终结,那么当前节点的 sum s u m 值所包含的信息一定与其有相同前缀,但 sum s u m 所包含的信息有可能刚好在该节点终结,所以要减去 end e n d 值。


代码:

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>  
#include<cstdlib>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<ctime>
#include<iomanip>
#include<string>
#include<bitset>
#define LL long long
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}
struct node{
    int end,cnt;
    int son[2];
    node()
    {
        son[1]=son[0]=0;
        end=cnt=0;
    }
}trie[500001];
int num=0;
char s[10001];
int a;
void in(char *s)
{
    int w;
    int f=0;
    for(int i=0;i<a;i++)
    {
        w=s[i]-'0';
        if(!trie[f].son[w])
          trie[f].son[w]=++num;
        f=trie[f].son[w];
        trie[f].cnt++;
    }
    trie[f].end++;
}
int ans=0;
void ask(char *s)
{
    int tf=0;
    int f=0;
    int w;
    for(int i=0;i<a;i++)
    {
        w=s[i]-'0';
        if(trie[f].son[w]==0)
        {
            tf=1;
            break;
        }
        f=trie[f].son[w];
        ans+=trie[f].end;
    }
    if(tf==0) ans=ans-trie[f].end+trie[f].cnt;
}
int main()
{
    int n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        a=read();
        for(int j=1;j<=a;j++)
        {
            int b=read();
            s[j-1]=char(b+48);
        }
        in(s);
    }
    for(int i=1;i<=m;i++)
    {
        a=read();
        for(int j=1;j<=a;j++)
        {
            int b=read();
            s[j-1]=char(b+48);
        }
        ans=0;
        ask(s);
        printf("%d\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值