字典树(Trie)

博客通过学习这篇博客(https://www.cnblogs.com/TheRoadToTheGold/p/6290732.html)+自己的理解写的

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

字典树其实主要就是查询,当你查询的时候,你其实是不知道你最开始是插入的是什么,只是通过特定的路径去判断这个字符串有没有出现过。所以一般由来查找字符串有没有出现过或者出现的次数。

什么时候用比较好,比如有n次输入m次查询,你就不能一次又一次的遍历,时间复杂度太高了,所以需要字典树,字典树的时间复杂度是O(m * n)

如何存储字符串呢?字典树储在一个树上,比如只含有26个字母的字符串,从跟开始每个节点最多有26个子节点。用树的枝干来存储数据。如图:

图片来自 https://www.cnblogs.com/TheRoadToTheGold/p/6290732.html

可以顺着枝干,找到cat,cash,app,apple,aply等,这就是构建字典树的过程。

如何建树?代码如图所示:

void Insert(char *s, int rt){
     len = strlen(s);
     for(int i = 0; i < len; i++){
        int id = s[i] - 'a';
        if(!trie[rt][id]){
            trie[rt][id] = ++ tot;
        }
        rt = trie[rt][id];
     }
}

那么查询呢?

bool Find(char *s, int rt){
    len = strlen(s);
    int cn = 0;
    for(int i = 0; i < len; i++){
        int id = s[i] -'a';
        if(trie[rt][id] != 0)cn++;
        rt = trie[rt][id];
    }
    if(cn = len)return true;
    else return false;
}

例题:

统计难题     HDU   1251

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1251

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 2000005
int trie[maxn][26];
int sum[maxn], len, tot;
char s[12];
void Insert(char *s, int rt){
     len = strlen(s);
     for(int i = 0; i < len; i++){
        int x = s[i] - 'a';
        if(!trie[rt][x]){
            trie[rt][x] = ++tot;
        }
        sum[trie[rt][x]]++;

        rt = trie[rt][x];
     }
}
int Find(char *s, int rt){
    len = strlen(s);
    for(int i = 0; i < len; i++ ){
        int x = s[i] - 'a';
        if(trie[rt][x] == 0)return 0;
        rt = trie[rt][x];
    }
    return sum[rt];
}
int main(){
    int rt = 0;
    tot = 0;
    while(cin.getline(s,12)){    //输入单词
         if(strlen(s)==0 || s[0]==' ')    //如果读入字符串的长度为0或者是空格,说明读入的是空行
            break;
        Insert(s, rt);
     }
     while(scanf("%s",s)!=EOF){
         printf("%d\n",Find(s, rt));    //返回s为前缀的单词的数量
     }
return 0;
}

Phone List   HDU   1671

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1671

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
#define N 111111
int trie[N][11];
int vis[N];
char s[15];
int  n, T, tot, rt, flag;
int Insert(char *s)
{
    int len = strlen(s);
    rt = 0;
    int ans = 0;
    for(int i = 0; i < len; i++)
    {
        int x = s[i] - '0';
        if(!trie[rt][x]){
                memset(trie[tot], 0, sizeof(trie[tot]));
                trie[rt][x] = tot++;
                ans ++;
        }
        else if(vis[trie[rt][x]])flag = 1;
        rt = trie[rt][x];

    }
    if(!ans)flag = 1;
    vis[rt]++;
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        tot = 1;
        flag = 0;
        memset(trie[0], 0, sizeof(trie[0]));
        memset(vis, 0, sizeof(vis));
        while(n--)
        {
            scanf("%s", s);
            if(!flag)
            {
                Insert(s);
            }
        }
        if(flag)printf("NO\n");
        else printf("YES\n");
    }

}

单词数    HDU  2072

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2072

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include <iostream>
#include <cstdio>
#include <sstream>
#include <set>
using namespace std;
#define N 100000
int trie[N][26];
int vis[N];
int tot, rt, len, ans;
void Insert(string s)
{
    rt = 0;
    len = s.size();
    for(int i = 0; i < len; i++)
    {
        int x = s[i] - 'a';
        if(!trie[rt][x])
        {
            memset(trie[tot], 0, sizeof(trie[tot]));
            trie[rt][x] = tot++;
        }
        rt = trie[rt][x];
    }
    if(!vis[rt]&& rt != 0)
    {
        ans ++;
    }
    vis[rt] = 1;
}
int main()
{
    string s;
    memset(vis, 0, sizeof(vis));
    memset(trie[0], 0, sizeof(trie[0]));
    tot = 1;
    ans = 0;
    while(getline(cin, s) && s != "#")
    {
        istringstream sin(s);
        string w;
        while(sin >> w)
            Insert(w);


    }
    cout << ans << endl;

    return 0;
}

题目:前缀统计

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int trie[maxn][26];
int sum[maxn], tot = 0;
char str[maxn];
void Insert(char *s, int rt)
{
    int len = strlen(s);
    for(int i = 0; i < len; i++)
    {
        int x = s[i] - 'a';
        if(!trie[rt][x])
        {
            trie[rt][x] = ++tot;
        }
        rt = trie[rt][x];

    }
    sum[rt]++;
}
int query(char *s, int rt)
{
    int len = strlen(s);
    int ans = 0;
    for(int i = 0; i < len; i++)
    {
        int x = s[i] - 'a';
        if(!trie[rt][x])return ans;
        if(trie[rt][x]) ans += sum[trie[rt][x]];
        rt = trie[rt][x];
    }
    return ans;
}
int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    while(n--)
    {
        scanf("%s", str);
        Insert(str, 0);
    }
    while(m--)
    {
        scanf("%s", str);
        printf("%d\n",query(str, 0));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值