统计难题 HDU - 1251
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.
Output
对于每个提问,给出以该字符串为前缀的单词的数量.
Sample Input
banana
band
bee
absolute
acm
ba
b
band
abc
Sample Output
2
3
1
0
字典树写法
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
struct node
{
struct node *next[26];
int num;
node()
{
for(int i=0; i<26; i++)
next[i]=NULL;
num=0;
}
};
node *root;
char str[21];
void Creat(char *s)
{
node *p=root;
int i,id,len=strlen(s);
for(i=0; i<len; i++)
{
id=s[i]-'a';
if(p->next[id]==NULL)
{
p->next[id]=new node;
}
p=p->next[id];
p->num++;
}
}
int Find(char *s)
{
node *p=root;
int i,id,len=strlen(s);
for(i=0; i<len; i++)
{
id=s[i]-'a';
if(p->next[id]==NULL)
{
return 0;
}
p=p->next[id];
}
return p->num;
}
void Delete(node *root)
{
if(root==NULL)
return;
for(int i=0; i<26; i++)
{
if(root->next[i])
Delete(root->next[i]);
}
delete(root);
}
int main()
{
int n,m,i;
root=new node;
while(gets(str))
{
if(str[0]=='\0')
break;
Creat(str);
}
while(gets(str))
{
printf("%d\n",Find(str));
}
Delete(root);
return 0;
}
使用静态内存存储字典树节点
这里简单介绍一下静态内存和动态内存
https://blog.youkuaiyun.com/LH2HA3/article/details/79051998?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159296793919725219921572%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159296793919725219921572&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_ctr_v2-1-79051998.ecpm_v1_rank_ctr_v2&utm_term=%E9%9D%99%E6%80%81%E5%86%85%E5%AD%98%E7%9A%84%E5%A5%BD%E5%A4%84
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct node
{
int num;//记录前缀出现次数
//int flag;
struct node *next[26];//26个字母,二十六个孩子
};
node a[1000000];//静态内存
int top;
node *creat() //构建子树
{
int i;
node *root = &a[top++];
root->num = 0;
//root->flag=0;
for(i = 0; i < 26; i++)
root->next[i] = NULL;
return root;
}
node *insert(node *root, char str[])
{
node *p = root;
int i, id;
for(i = 0; str[i]; i++)//将字符串的字符一个一个放入字典树的结点中
{
id = str[i] - 'a';//下标,0到25分别对应这二十六个字母
if(!p->next[id])
p->next[id] = creat();//如果对应下标的孩子结点是空,新建字树‘
p = p->next[id];//否则就继续
p->num++;
}
//p->flag++;//让字符串中最后一个字符,对应的结点的data值++,记录该字符出现了几次
return root;
}
int Find(node *root, char str[])
{
node *p = root;
int i, id;
for(i = 0; str[i]; i++)
{
id= str[i] - 'a';
if(!p->next[id])//如果是空,代表没有出现此前缀,ruturn 0
return 0;
p = p->next[id];//否则继续
}
return p->num;//字符串循环成功走完
}
int main()
{
int n, m, i;
char str[12];
top = 0;//用来控制使用静态内存
node *root = creat();//先建好字典树的根,空的字典树
while(gets(str))
{
if(str[0]=='\0')
break;
root = insert(root, str);//构建字典树
}
while(gets(str))
{
printf("%d\n",Find(root,str));
}
return 0;
}
利用结构体写法
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct node
{
int num;
//int flag;
int next[26]; //0-25对应a-z,也是记录本字母的指向的空间,代表字典中有这个单词的这个字母
} a[1000000]; //a相当于一个个的节点,从0开始一个一个增加,
int top; //top 相当于创建节点的p一样,创建一个个节点
int Creat()
{
memset(a[top].next,0,sizeof(a[top].next));
a[top].num=0;
//a[top].flag=0;
top++; //节点移动
return top-1; //返回上一个创造的那个节点的坐标
}
void insert(int root,char s[]) //进行插入,就是创造一个字典,这里的root一直都是0
{
int i,id;
for(i=0; s[i]; i++)
{
id=s[i]-'a';
if(a[root].next[id]==0)
a[root].next[id]=Creat(); //如果=-1说明这个单词的这个字母在字典中没有
root=a[root].next[id];
a[root].num++;
}
//a[root].flag++;//判断下一个字母之前是否存在,最后记录这个单词的这个字母,让它++,看看这个单词出现几次
}
int Find(int root,char s[])
{
int i,id;
for(i=0; s[i]; i++)
{
id=s[i]-'a';
if(a[root].next[id]==0)
return 0; //如果等于-1说明这个单词的这个字母字典中没有,那么这个单词就
root=a[root].next[id]; //没有,返回0,如果有进行判断下一个字母
}
return a[root].num; //直到判断完全部字母,然后返回最后一个字母的次数
}
int main()
{
int n,m;
char str[12];
int root;
top=0;
int i;
root=Creat(); //先弄一个空间,root=0;
while(gets(str))
{
if(str[0]=='\0')
break;
insert(root, str);//构建字典树
}
while(gets(str))
{
printf("%d\n",Find(root,str));
}
return 0;
}
利用二维数组
#include <bits/stdc++.h>
using namespace std;
char s[20];
int trie[500010][26]; //数组开的很玄学,大一点TLE小一点RTE
int a[500010]; //用来计数前缀
int b[500010]; //用来记录单词结尾
int f = 1;
void insert()
{
int len = strlen(s);
int p = 0;
for(int i=0; i<len; i++)
{
int id = s[i] - 'a';
if(trie[p][id]==0)
{
memset(trie[f], 0, sizeof(trie[f]));
trie[p][id] = f; //二位数组记录的是子节点的位置
f++;
}
p = trie[p][id];
a[p]++;
}
//b[p]++;
}
int Find()
{
int len = strlen(s);
int p = 0;
for(int i = 0; i<len; i++)
{
int id = s[i] - 'a';
if(trie[p][id] == 0)
return 0;
p = trie[p][id];
}
return a[p];
}
int main()
{
int n, m;
memset(trie[0], 0, sizeof(trie[0]));
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
f = 1;
while(gets(s))
{
if(s[0]=='\0')
break;
insert();
}
while(gets(s))
{
printf("%d\n",Find());
}
return 0;
}
使用map写法,自行了解。
#include<stdio.h>
#include<string>
#include<string.h>
#include<map>
#include<iostream>
using namespace std;
map<string,int>q;
int main()
{
char a[20];
while(gets(a))
{
if(a[0]=='\0')
break;
int l=strlen(a);
string s;
for(int i=0;i<l;i++)
{
s+=a[i];//每一个字符串从开始依次增加的字符串存储下来;
q[s]++;//记录出现的次数;
}
}
string s;
while(cin>>s)
cout<<q[s]<<endl;
return 0;
}
本文详细介绍了如何使用字典树(Trie)解决一个统计特定前缀单词数量的问题,包括字典树的构建、查找及不同实现方式,如动态内存分配、静态内存存储、结构体与二维数组应用。
411

被折叠的 条评论
为什么被折叠?



