字典树讲解 总结

学习于:
https://www.cnblogs.com/-citywall123/p/11178106.html

一.概述

字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

二.理解

在这里插入图片描述
在这里插入图片描述

  • 字典树用表示字母

  • 有相同前缀的单词公用前缀节点,那我们可以的得出每个节点最多有26个子节点(在单词只包含小写字母的情况下)

  • 整棵树的根节点是空的。为什么呢?便于插入和查找,这将会在后面解释。

  • 要对单词最后一个字母的vis[i]置为true,其他的都是false。true代表这个单词结束。

三.模板

A.插入单词

从图中可以直观看出,从左到右扫这个单词,如果字母在相应根节点下没有出现过,就插入这个字母;否则沿着字典树往下走,看单词的下一个字母。

设数组trie[i][j]=k,表示编号为i的节点的第j个孩子是编号为k的节点

i,k表示节点的位置编号,这是相对整棵树而言的;
j,表示节点i的第j的孩子,这是相对节点i而言的。(ASCII)

因为每个节点最多有26个子节点,我们可以按他们的字典序从0——25编号,也就是他们的ASCLL码 - ’a’。相同字母的编号相同

代码:

void insertt(char *a)//插入单词s
{
    int len=strlen(a);//单词s的长度
    int root=0;//根节点编号为0
    for(int i=0;i<len;i++)
    {
        int id=a[i]-'a';//第二种编号
        if(tree[root][id]==0)//如果之前没有从root到id的前缀 
             {
                  tree[root][id]=++tot;//插入,tot为总节点数
                  root=tot;
             }
         else root=tree[root][id];//顺着字典树往下走
        //num[root]++;///num[i]以第i个节点所代表的前缀为前缀的单词个数
    }
    //vis[root]=1;///对单词最后一个字母的vis[i]置为1,其他的都是初始值0
}

B.查找

代码

bool findd(char *s)
{
    int len=strlen(s);
    int root=0;//从根结点开始找
    for(int i=0;s[i];i++)
    {
        int x=s[i]-'a';
        if(tree[root][x]==0)   return false;//以root为头结点的x字母不存在,返回0 
        root=tree[root][x];//往下走 
    }
    return true;
}

C.初始化

void init()
{
    for(int i=0;i<=tot;i++)
    {
        memset(tree[i],0,sizeof(tree[i]));
        num[i]=vis[i]=0;
    }
    tot=0;
}

四.应用

The 2019 ICPC Asia Shanghai Regional
链接:https://ac.nowcoder.com/acm/contest/4370/B
代码1:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
int tree[100008][12],num[100008],vis[100008];
int tot=0;
void insertt(char *a)//插入单词s
{
    int len=strlen(a);//单词s的长度
    int root=0;//根节点编号为0
    for(int i=0;i<len;i++)
    {
        int id=a[i]-'0';//第二种编号
        if(tree[root][id]==0)//如果之前没有从root到id的前缀
             {
                  tree[root][id]=++tot;//插入,tot为总节点数
                  root=tot;
             }
         else root=tree[root][id];//顺着字典树往下走
        num[root]++;///num[i]以第i个节点所代表的前缀为前缀的单词个数
    }
    vis[root]=1;///对单词最后一个字母的vis[i]置为1,其他的都是初始值0
}
bool findd(char *s)
{
    int len=strlen(s);
    int root=0;//从根结点开始找
    for(int i=0;s[i];i++)
    {
        int x=s[i]-'0';
        if(tree[root][x]==0)   return false;//以root为头结点的x字母不存在,返回0
        root=tree[root][x];//往下走
    }
    return true;
}
void init()
{
    for(int i=0;i<=tot;i++)
    {
        memset(tree[i],0,sizeof(tree[i]));
        num[i]=vis[i]=0;
    }
    tot=0;
}
int main()
{
    char f[15];
    int T,n;
    cin >> T;
    for(int k = 1; k <= T; k ++)
    {
        int flag=1;
        init();
        scanf("%d",&n);
        for(int j=1;j<=n;j++)
        {
            scanf("%s",f);
            insertt(f);
        }
        for(int i=0;i<=tot;i++)
        {
            if(vis[i]&&num[i]>1)///是整个单词,并且这整个单词是前缀
            {
                flag=0;
                break;
            }
        }
        if(flag==0)printf("Case #%d: No\n",k);
        else printf("Case #%d: Yes\n",k);
    }
    return 0;
}

代码2:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
int tree[100008][12],num[100008],vis[100008];

int tot=0;
void insertt(char *a)//插入单词s
{
    int len=strlen(a);//单词s的长度
    int root=0;//根节点编号为0
    for(int i=0;i<len;i++)
    {
        int id=a[i]-'0';//第二种编号
        if(tree[root][id]==0)//如果之前没有从root到id的前缀
             {
                  tree[root][id]=++tot;//插入,tot为总节点数
                  root=tot;
             }
         else root=tree[root][id];//顺着字典树往下走
        num[root]++;///num[i]以第i个节点所代表的前缀为前缀的单词个数
    }
    vis[root]=1;///对单词最后一个字母的vis[i]置为1,其他的都是初始值0
}
int findd(char *s)
{
    int root=0;//从根结点开始找
    for(int i=0;s[i];i++)
    {
        int x=s[i]-'0';
        if(tree[root][x]==0) return 0;//以root为头结点的x字母不存在,返回0
        root=tree[root][x];//往下走
    }
    return num[root];
}
void init()
{
    for(int i=0;i<=tot;i++)
    {
        memset(tree[i],0,sizeof(tree[i]));
        num[i]=vis[i]=0;
    }
    tot=0;
}
char word[100008][12];
int main()
{

    int T,n;
    cin >> T;
    for(int k = 1; k <= T; k ++)
    {
        memset(word, 0, sizeof(word));
        int flag=1;
        init();
        scanf("%d",&n);
        for(int i = 1; i <= n; ++i)
        {
            scanf("%s", &word[i]);
            insertt(word[i]);
        }
        for(int i = 1; i <= n; ++i)
        {
            if(flag)
            {
                if(findd(word[i]) > 1)
                {
                    flag = 0;
                    break;
                }
            }
        }
        if(flag==0)printf("Case #%d: No\n",k);
        else printf("Case #%d: Yes\n",k);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值