luogu2922秘密消息【字典树】

本文深入探讨了一种利用字典树进行高效信息与密码匹配的算法。通过构造字典树来存储信息,并在树中记录传递和结束状态,使得算法能够快速判断密码是否为信息的前缀,反之亦然。文章详细介绍了算法的具体实现过程,包括字典树的构建、信息的存储以及密码的匹配流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

>Description
奶牛共有M(1≤M≤50000)条二进制的消息,知道了第i条二进制信息的前bi(l《bi≤10000)位。
同时奶牛使用N(1≤N≤50000)条密码,我们仅仅了解第J条密码的前cj(1≤cj≤10000)位。

对于每条密码J,都可能有信息能够和它匹配。
匹配的标准是:第J条密码是第i条信息的前缀,或第i条信息是第J条密码的前缀。
答案输出每条密码能够与信息匹配的数量

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


>Input
第1行:两个整数:M和N.
接下来M行:第i + 1行描述截取的代码i,其中包含整数b_i,表示信息长度,后跟b_i个由空格分隔的0和1
接下来N行:第M + j + 1行描述代码字j,其中包含整数c_ j,表示密码长度,后跟c_j个空格分隔的0和1

>Output
第1…N行:行j:第J条密码可以匹配的信息数。


>Sample Input
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

>Sample Output
1
3
1
1
2


>解题思路
终于做出来了

直接用字典树储存每条信息。储存过程中要记录pass与end,pass表示有多少条信息经过了这个点,end则为有多少条信息是在这个点结束的。

后枚举每条密码,用字典树进行查询:
1.每次加上当前的end(以信息为前缀)
2.如果密码还没有遍历完,但是下面没有“路”了,就直接输出(搜不下去了呗就推出)
3.如果密码遍历完了,答案减去当前的end再加上当前的pass(以密码为前缀)

标风优美


>代码

#include<iostream>
#include<cstdio>
using namespace std;
struct son //储存儿子
{
	int s[2]; //s[0]为0儿子,s[1]为1儿子
} a[500005];
int n, m, c, t, now, ans, x[10005], end[500005], pass[500005];
int main()
{
	scanf ("%d%d", &n, &m);
	t = 1; //记录节点数
	for (int i = 1; i <= n; i++)
	{
		now = 1;
		scanf ("%d", &c);
		for (int j = 1; j <= c; j++)
		{
			scanf ("%d", &x[j]);
			if (a[now].s[x[j]] != 0) now = a[now].s[x[j]]; //如果有就直接转移
			else
			{
				t++;
				a[now].s[x[j]] = t;
				now = t; //否则节点数++
			}
			pass[now]++;
		}
		end[now]++;
	}
	for (int i = 1; i <= m; i++)
	{
		now = 1;
		ans = 0;
		scanf ("%d", &c);
		for (int j = 1; j <= c ; j++)
		  scanf ("%d", &x[j]);
		for (int j = 1; j <= c ; j++)
		{
			now = a[now].s[x[j]]; //转移
			if (now == 0) break;
			ans += end[now];
			if (j == c) ans = ans - end[now] + pass[now];
		}
		printf ("%d\n", ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值