统计难题 HDU - 1251 (多种书写方法)

本文详细介绍了如何使用字典树(Trie)解决一个统计特定前缀单词数量的问题,包括字典树的构建、查找及不同实现方式,如动态内存分配、静态内存存储、结构体与二维数组应用。

统计难题 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值