Trie树模板题 P2016

Description

给出n个单词组成的字典(可能由相同的单词),请你完成下列任务:


任务1、把n个单词去重后按字典序由小到大后输出。


任务2、给出m个询问,每次询问一个单词是否在字典中存在,如果存在,输出该单词在字典中出现的次数。


Input

第一行为n和m.接下来的n行,每行一个单词。中间空一行。  在接下来的m行,每行一个单词,表示一个询问。


Output

开始的n行为任务1的输出结果。空一行后输出m行,每行一个整数,表示询问的结果(不存在,则输出0)。


Hint

n,m<=50000所有单词由大小写英文字母组成,长度不超过50。


Solution

存trie树的过程就已经将字符串去重并且统计次数了。用一个结构体把tree数组存成ch[n],表示这个结点的字符的下一个结点是哪个,end_line标记次数,或者标记是否是一个完整字符,也可以标记是前缀还是完整字符还是不能匹配。输出去重后的单词就是遍历一次trie树,dofind的过程和insert的过程几乎一致,只是有了一个返回值返回出现的次数。


注意事项:
1、输出dfs_trie之后还要打一个回车。
2、由于是单词所以存trie树和dofind的过程要区分大小写。
3、循环的过程中lenofS要取等。
4、开的maxm只有52那么如果在dfs_trie的过程中就不能取52因为它会直接跳到end_line然后陷入死循环。所以maxm开成什么53,54这种就能避免这种问题。
5、maxn要开到50000*50那么大。
6、噢我还是傻逼。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxm 52
#define maxn 2500005
using namespace std;
struct Trie{
	int ch[maxm];
	int end_line;
}tree[maxn];
char S[maxn],ans[maxn];
int n,m,newp;
void insert(){
	int x=0;int lenofS=strlen(S+1);
	for(int i=1;i<=lenofS;i++){
		int y;
		if(S[i]>='A'&&S[i]<='Z'){
			y=S[i]-'A';
		}
		if(S[i]>='a'&&S[i]<='z'){
			y=S[i]-'a'+26;
		}
		if(tree[x].ch[y]==0){
			tree[x].ch[y]=++newp;
		}
		x=tree[x].ch[y];
	}
	tree[x].end_line++;
}
char getword(int d){
	if(d<26)return d+'A';
	else return d-26+'a';
}
void dfs_trie(int s,int l){
	if(tree[s].end_line){
		ans[l]=0;
		printf("%s\n",ans+1);
	}
	for(int i=0;i<52;i++){
		if(tree[s].ch[i]){
			ans[l]=getword(i);
			dfs_trie(tree[s].ch[i],l+1);
		}
	}
}
int dofind(){
	int x=0;int lenofS=strlen(S+1);
	for(int i=1;i<=lenofS;i++){
		int y;
		if(S[i]>='A'&&S[i]<='Z'){
			y=S[i]-'A';
		}
		if(S[i]>='a'&&S[i]<='z'){
			y=S[i]-'a'+26;
		}
		if(tree[x].ch[y]==0){
			return 0;
		}
		x=tree[x].ch[y];
	}
	return tree[x].end_line;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",S+1);
		insert();
	}
	dfs_trie(0,1);
	putchar('\n');
	for(int i=1;i<=m;i++){
		scanf("%s",S+1);
		int o=dofind();
		printf("%d\n",o);
	}
	return 0;
}
posted @ 2018-11-30 16:48  虚拟北方virtual_north。  阅读(205)  评论(0编辑  收藏  举报