Trie树(详解+例题)

本文介绍了Trie树的基本概念,包括其工作原理、空间换时间的优势、应用场景以及如何通过代码实现插入和查找操作。以ACWing题库中的例题为例,展示了Trie树在字符串统计中的应用。
摘要由CSDN通过智能技术生成

1、介绍Trie树

Trie树,即字典树,又称单词查找树或键树,是一种树形结构,每个节点保存一个字符,一条路径表示一个字符串。
   它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较。

Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

2、应用及基本性质

  • 要么存储小写字母/大写字母

  • 要么存储数字

  • 要么存储0或1

  • 不会出现混着存的情况(比如:既存大写字母又存小写字母还存数字这种)

  1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符。

  2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。

  3. 每个节点的所有子节点包含的字符都不相同。

3、实现

  • 根据上述例子:过程如下(以abcdef,abdef,abc为例子)其他同理

  • 首先创建根节点root,刚开始根节点不存在a,所以创建a节点

  • 接着往下走,在a节点下插入字符b,节点a下不存在子节点b,创建节点b。

  • 继续走,在b节点下插入字符c,节点b下不存在子节点c,创建节点c。

  • 接着上述过程(假设创建到最后一个),我们创建到了f节点,此字符串结束,在末尾做一个标记,插入字符串abcdef完成


  • 再插入字符串abdef

  • 首先插入a节点,发现root下有子节点a,所以不用创建,继续。

  • 插入字符b节点,发现a下有子节点b,所以不用创建,继续。

  • 插入字符d节点,发现b下没有子节点d,所以创建d子节点,继续。

  • 插入字符e节点,发现d节点下没有子节点e,所以创建子节点e,继续。

  • 插入字符f节点,发现e节点下没有子节点f,所以创建子节点f,继续。

  • 插入结束,在字符串结尾做一个标记,表示完成插入字符串


  • 插入abc

  • 首先插入a节点,发现root下有a节点,所以不用创建,继续。

  • 再插入b节点,发现a节点下的子节点也有b,所以不用创建,继续。

  • 再插入c同样,发现b节点下的子节点有c,所以不用创建,完成 插入,做一个标记。


3.1、代码模板
int idx; //各个节点的位置,编号
int son[N][26]; //trie树
int cnt[N];//标记以某个编号结尾的单词的个数
//插入
void insert(string s)
{
    int p = 0; //刚开始指向根部节点(root)
    for(int i=0;i<s.size();i++)
    {
        //将字母映射成数字(a->0,b->1,c->2)依次类推z->25;
        int u = s[i] - 'a';
        //如果当前没有此节点,创建这个节点,分配位置(编号)
        if(!son[p][u]) son[p][u] = ++idx; //新节点的编号是idx+1
        p = son[p][u];
    }
    cnt[p]++;
}
//查找单词操作
int query(string s)
{
    int p = 0; //指向根节点
    for(int i=0;i<s.size();i++)
    {
        //将当前字符转换成数字(a->0, b->1,...)
        int u = s[i] - 'a';
        //如果走不通了,即树中没有保存当前字符
        //则说明树中不存在该字符串
        if(!son[p][u]) return 0;
        p = son[p][u];//指向下一个节点
    }
    //循环结束的时候,p 等于字符串 s 的尾字符所对应的 idx
    // cnt[p] 就是字符串 s 出现的次数
    return cnt[p];
}

4、例题835. Trie字符串统计 - AcWing题库

 


AC代码:
#include<iostream>
#include<cstring>

using namespace std;

const int N = 1e5;
int idx;
int son[N][26];
int cnt[N];

//插入
void insert(string s)
{
    int p = 0;//指针指向
    for(int i=0;i<s.size();i++)
    {
        int u = s[i] - 'a'; //映射
        //如果不存在该节点,创建一下
        //新节点编号idx+1
        if(!son[p][u]) son[p][u] = ++idx;
        p = son[p][u];//指向下一个节点
    }
    cnt[p]++;
}

//查找
int query(string s)
{
    int p = 0;
    for(int i=0;i<s.size();i++)
    {
        int u = s[i] - 'a';
        if(!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

int main()
{
    int n;
    scanf("%d", &n);
    while(n --)
    {
        string op,str;
        cin >> op >> str;
        if(op == "I")
        {
            insert(str);
        }
        else if(op == "Q")
        {
            int res = query(str);
            cout << res << endl;
        }
    }
    return 0;
}

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值