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

题目:###


题目链接:[USACO08DEC]秘密消息Secret Message

题意:##

给定n条01信息和m条01密码,对于每一条密码A,求所有信息中包含它的信息条数和被它包含的信息条数的和。

分析:##

建立一棵trie树,类似于存储26个字母一样存储0和1(每个节点只有两个儿子),然后设包含节点p的信息条数有size[p]条,在节点p结束的信息条数有end[p]条,节点的两个儿子的编号为num[p][0]和num[p][1],然后存储信息。
我们容易知道size[p]存储的实际上是包含了“从根节点走到p节点所代表的这条字符串”的所有字符串的总数(包括这条字符串自己)
对于每一条读入的密码,我们把它存储在一个临时数组里,然后在这棵trie树上往下找,记录一个ans,走过每一个节点的时候ans+=end[p]。
走的时候会有两种情况:

  • 这条路比这个密码短(即所有符合条件的信息都是这条密码的子集),那么走到尽头就可以了
  • 这条路比这个密码长,那么走到这条密码的尽头(这条密码的最后一个字符对应的节点)的时候ans+=size[p](见上面的解释),其余时间ans+=end[p]就可以了

代码不长,我用结构体写的trie树(和上面分析的变量名字是一样的),然后不知道哪里的玄学优化(register?)起了作用好像跑得还有点快(…)
里面的注释自认为比较齐全了


代码:###

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int cnt=0,f=1;char c;
	c=getchar();
	while(!isdigit(c)){
		if(c=='-')f=-f;
		c=getchar();
	}
	while(isdigit(c)){
		cnt=cnt*10+c-'0';
		c=getchar();
	}
	return cnt*f;
}
int n,m,len,tot;//tot:记录节点编号 
struct node{
	int end;//记录有多少条信息在这个节点完结 
	int size;//记录有多少条信息过这个节点 
	int num[2];//记录左右儿子编号 
}trie[500005];
int a[50005];//临时数组,用于存放每次添加的信息或密码 
inline void insert(int k){
	int p=0;
	for(register int i=1;i<=k;p=trie[p].num[a[i]],i++){
		if(!trie[p].num[a[i]])trie[p].num[a[i]]=++tot;
		trie[p].size++;
	}
	trie[p].size++;
	trie[p].end++; 
}
inline int ask(int k){
	int p=0;
	int ans=0;
	for(register int i=1;i<=k;p=trie[p].num[a[i]],i++){
		int to=trie[p].num[a[i]];
		if(!to)break;//如果这条路上没有更长的信息(走不动了)就不搜了 
		if(i==k)ans+=trie[to].size;//如果这条密码走到头了就把包含它的串的个数都加上(这里包含了和它一样的串) 
		else
		ans+=trie[to].end;//如果没有走到头就把到这个节点完结的串的个数加上 
	}
	return ans;
}
int main(){
	m=read();n=read();
	for(register int i=1;i<=m;i++){
		len=read();
		for(register int j=1;j<=len;j++)a[j]=read();
		insert(len);
	}
	for(register int i=1;i<=n;i++){
		len=read();
		for(register int j=1;j<=len;j++)a[j]=read();
		printf("%d
",ask(len));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/kma093/p/9880498.html