Trie

作用

Trie是前缀树,通俗的来说就是一棵字符树,它的作用是把许多字符串整合储存,做成一个字符串集合,有时可以达到快速查找的效果(比如求多少个单词是一个句子的前缀)。

实现

比如有she,her,hers,his,him这几个单词,我们就可以建立这么一个Trie。
这里写图片描述
首先会发现Trie的一个特性:必有一个根节点(把根节点记为0)。然后每条边都有一个权值,权值即为字符(一般用取id的方法来当成权值)。然后那些红色的是什么东西?不难想到这几个节点就是单词节点,也就是存在字符串的节点。

那么记录son[i][j]表示i节点j编号的儿子(没有就给0,尽管0节点是根节点,但是并没有什么关系,因为不可能有边指向根节点),以及num[i]表示i节点的权值,就可以构造一棵Trie了。

Trie构造的时间复杂度为ΣL,L表示字符串的长度,询问时间为Trie的最大深度(然而实际处理中这两个操作的时间复杂度可能会有所降低)。Trie不仅有高效的时间复杂度,还为AC自动机等算法提供了基础。

拓展

不难发现Trie的空间复杂度为O(ΣL*id),id表示字符个数。当id范围为整个char时,开销是十分高昂的。如果这时候时间要求比较宽松,可以使用邻接表来减小空间开销(这样就保证没有的儿子不记录,则空间复杂度减少为O(ΣL)),但是时间开销上涨了(因为搜索的时候你要判断是哪个儿子)。所以,权衡空间和时间是很重要的。

模板

#include<cstdio>
const int maxn=100000,maxl=10,maxi=26; //maxi表示字符总数

struct Trie
{
    int son[maxn*maxl+5][maxi],w[maxn*maxl+5],len;
    int geti(char ch) {return ch-'a';} //得到字符标号,这里默认只有小写字母
    void Insert(char *s) //插入s
    {
        int now=0; //now表示当前位置
        for (int i=1;s[i];i++)
        {
            if (son[now][geti(s[i])]==0) son[now][geti(s[i])]=++len; //并没有出现过,加上
            now=son[now][geti(s[i])]; //继续遍历
        }
        w[now]++; //最终位置now就是单词节点
    }
    int get_pre(char *s) //查找s的前缀个数
    {
        int now=0,num=0; //num表示数量
        for (int i=1;s[i];i++)
        {
            num+=w[now]; //加上now节点的单词数量
            if (son[now][geti(s[i])]!=0) now=son[now][geti(s[i])]; else break;
        }
        return num;
    }
};
int n,m;
Trie tr;
char now[maxl+5];

bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int reads(char *s)
{
    int len=0;char ch=getchar();if (ch==EOF) return EOF;
    s[++len]=ch;while (!Eoln(s[len])) s[++len]=getchar();s[len--]=0;
    return 0;
}
void readln() {scanf("%*[^\n]");getchar();}
int main()
{
    freopen("Trie.in","r",stdin);
    freopen("Trie.out","w",stdout);
    scanf("%d",&n);readln();
    for (int i=1;i<=n;i++) reads(now),tr.Insert(now);
    scanf("%d",&m);readln();
    for (int i=1;i<=m;i++) reads(now),printf("%d\n",tr.get_pre(now));
    return 0;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值