Trie字典树学习

本文详细介绍了Trie树的结构及其在存储多个字符串时的优势,通过实例展示了如何利用Trie树进行字符串的高效插入与快速查询。重点讨论了Trie树的时间和空间复杂度,并提供了具体代码实现,帮助读者理解其实现原理。
摘要由CSDN通过智能技术生成

Trie树的作用是存储多个字符串, 并按照字典序排好。算法时间复杂度为O(n), 空间复杂度为O(nk), n为所有字符串的长度和, k为可能出现的字符个数。 Trie树的实现就是按照单词的字母顺序从上到下依次连接(注意根节点为空)

字典树最常见的操作是插入和查询,

其中插入是对字典树的构建,基本思路是从头遍历字符串,若发现在Trie树中已经有了相应结点,则向下走,否则新建结点

查询一般来说是求前缀多少的,基本思路也是从头遍历字符串,若能在Trie树中找到相应结点就继续往下走,找不到就return 0

下面代码是Trie树的数组方式实现:

const int alpha = 26;
const int maxn = 5e5;
struct Trie
{
    int ch[maxn][alpha];
    int val[maxn];
    int sz;
    Trie(){
        sz = 1;
        memset(ch[0], 0 , sizeof(ch[0]));
    }
    int idx(char c) {return c - 'a';}
    void insert(char *s)
    {
        int u = 0, n = strlen(s);
        for(int i = 0; i < n; i++){
            int c = idx(s[i]);
            if(!ch[u][c]){
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
            val[u]++;
        }
    }

    int findout(char *s)
    {
        int u = 0, cnt = 0, n = strlen(s);
        for(int i = 0; i < n; i++){
            int c = idx(s[i]);
            if(!ch[u][c])   return 0;
            u = ch[u][c];
        }
        return val[u];
    }
}a;

其中alpha代表小写的26个英文字母, maxn为字符串总长度, sz为结点总数, ch[i][j]表示结点i的编号为j的子节点,如ch[i][0]表示结点i的子节点a,val[i]表示结点i的附加信息

为了表明模板的正确性,用HDU 1251来测试一下:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int alpha = 26;
const int maxn = 5e5;
struct Trie
{
    int ch[maxn][alpha];
    int val[maxn];
    int sz;
    Trie(){
        sz = 1;
        memset(ch[0], 0 , sizeof(ch[0]));
    }
    int idx(char c) {return c - 'a';}

    void insert(char *s)
    {
        int u = 0, n = strlen(s);
        for(int i = 0; i < n; i++){
            int c = idx(s[i]);
            if(!ch[u][c]){
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
            val[u]++;
        }
        //val[u]++;
    }

    int findout(char *s)
    {
        int u = 0, cnt = 0, n = strlen(s);
        for(int i = 0; i < n; i++){
            int c = idx(s[i]);
            if(!ch[u][c])   return 0;
            u = ch[u][c];
        }
        return val[u];
    }
}a;

int main()
{
    char s[11];
    gets(s);
    while(s[0] != '\0'){
        a.insert(s);
        gets(s);
    }
    while(~scanf("%s", s)){
        cout << a.findout(s) << '\n';
    }
    return 0;
}

再来感受一下HDU--1247吧,用的另一种数组写法:

#include<bits/stdc++.h>
using namespace std;

 const int maxn = 50005;
int tree[maxn][26];
int idx(char c){return c-'a';}
int tot = 1;
bool is_word[maxn];

void Insert(char s[],int u) //插入字符串s
{
    for(int i=0;s[i];++i){
        int c = idx(s[i]);
        if(!tree[u][c])
            tree[u][c] = ++tot;
        u = tree[u][c];
    }
    is_word[u] = true; //标记这个单词结束
}
bool Find(char s[],int u) //查询s是否是前缀
{
    for(int i=0;s[i];++i){
        int c = idx(s[i]);
        if(!tree[u][c])
            return false;
        u = tree[u][c];
    }
    return is_word[u];
}
char s[maxn][20],ns[20];

int main()
{
    int n = 0;
    while(scanf("%s",s[n])!=EOF){
        Insert(s[n++],1);
    }
    for(int i=0;i<n;++i){
        for(int j=1;s[i][j];++j){
            memcpy(ns,s[i],sizeof(s[i])); //将s[i]复制到ns中
            ns[j] = 0;
            if(Find(ns,1)&&Find(s[i]+j,1)){ //s[i]+j:从s[i]的第j个开始
                //将s分成两部分进行分别查找
                cout << s[i] << '\n';
                break;
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值