BZOJ1590:[Usaco2008 Dec]Secret Message秘密信息

浅谈\(Trie\)https://www.cnblogs.com/AKMer/p/10444829.html

题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=1590

把秘密信息建一棵\(Trie\),在节点上记录经过这个结点的字符串\(sum\)一共有多少个(也就是以从根到当前结点为前缀的字符串一共有多少个),记录以当前节点为字符串结尾\(bo\)的有多少个(也就是从根开始到当前结点的字符串一共有多少个)。

对于每一个密码,从根开始,把沿途所有的\(bo\)算进答案里(这些秘密信息是这条密码的前缀),最后到达的结点的\(sum\)加进答案里(这些秘密信息的前缀是这条密码),注意最后落脚点的\(bo\)要减去。

时间复杂度:\(O(\sum B_i+\sum C_i)\)

空间复杂度:\(O(\sum B_i+\sum C_i)\)

代码如下:

#include <cstdio>
using namespace std;

const int maxn=5e5+5;

int num[maxn];
int n,m,cnt,ans;

int read() {
	int x=0,f=1;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
	return x*f;
}

struct Trie {
	int tot;
	int bo[maxn];
	int sum[maxn];
	int son[maxn][2];

	void ins() {
		int pos=1;
		for(int i=1;i<=cnt;i++) {
			if(son[pos][num[i]])pos=son[pos][num[i]];
			else pos=son[pos][num[i]]=++tot;sum[pos]++;
		}
		bo[pos]++;
	}

	void find() {
		int pos=1;
		for(int i=1;i<=cnt;i++) {
			if(son[pos][num[i]])pos=son[pos][num[i]];
			else return;ans+=bo[pos];
		}
		if(bo[pos])ans-=bo[pos];ans+=sum[pos];
	}
}T;

int main() {
	n=read(),m=read(),T.tot=1;
	for(int i=1;i<=n;i++) {
		cnt=read();
		for(int j=1;j<=cnt;j++)
			num[j]=read();
		T.ins();
	}
	for(int i=1;i<=m;i++) {
		cnt=read(),ans=0;
		for(int j=1;j<=cnt;j++)
			num[j]=read();
		T.find();printf("%d\n",ans);
	}
	return 0;
}
posted @ 2019-02-27 19:54  AKMer  阅读(168)  评论(0编辑  收藏  举报