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;
}