字典树(Trie)学习笔记

什么是字典树


上图来自luogu题解ios

这是一种字典树,不过本文讲的不是这种图,本文要讲一种更通俗易懂的(博主我的观点)spa

我要讲的是每一个节点只存一个字母或数字,经过打标记的方法实现find的指针

像这样

上图来自百度百科code

如何存储字典树

我不想写那些很难搞的指针,虽然用指针会使程序简单明了,可能之后会更新上指针版的吧,咕咕咕blog

我的认为存储字符串只须要按顺序就能够了,这应该也是字典树的精髓字符串

如要顺序存储下面的字符串:"lyqalioi","orzliuzitong","zbqisredsun","orztzt".......get

只需用tree[i][j]来表示以i为根的j号孩子的编号(仔细搞搞这里,比较难懂)
用flag[i]来表示以i号节点为结束的字符串有没有(前面的描述不太准确,就是到i号节点的字符串有没有)
而后就能够迭代根编号root了博客

void add(char *s)
{
    int len=strlen(s),root=0/*root为根节点,初始化0*/,id/*id是编号用char-'a'表示*/;
    for(int i=0;i<len;++i)
    {
        id=s[i]-'a';
        if(!tree[root][id]) tree[root][id]=++sum;
        root=tree[root][id];//更新根节点,以前在博客里写了 
    } 
    flag[root]=true;//更新标记 
}

如何查找字符串有没有出现

若是完全懂了上面这里就不用看了string

这里就不解释了和上面同样

bool find(char *s)
{
    int len=strlen(s),root=0/*root为根节点,初始化0*/,id/*id是编号用char-'a'表示*/;
    for(int i=0;i<len;++i)
    {
        id=s[i]-'a';
        if(!tree[root][id]) return false;
        root=tree[root][id];
    }
    if(flag[root]==true) return true;//看有没有出现以root结束的字符串 
    else return false;
}

第一个图的那种线段树

代码比较复杂,不过效率比上文的高不少,因此本文也会讲解

应用

大多数字典树的题目都不会直接让你搞一棵树就完一般会带有一些附加条件,会在下面的例题中有涉及

例题

1.统计难题

连接

就是统计了个sum[root]没什么好说的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<string>
#include<cstring>
#define int long long int
using namespace std;
inline int read() {
    char c = getchar();
    int x = 0, f = 1;
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
const int MAXN=2e6+5;
int tree[MAXN][30],tot,sum[MAXN];
void add(char *s) {
    int root=0,id,len=strlen(s);
    for(int i=0; i<len; ++i) {
        id=s[i]-'a';
        if(!tree[root][id])  tree[root][id]=++tot;
        sum[tree[root][id]]++;
        root=tree[root][id];
    }
}
int find(char *s) {
    int root=0,id,len=strlen(s);
    for(int i=0; i<len; ++i) {
        id=s[i]-'a';
        if(!tree[root][id]) return 0;
        root=tree[root][id];
    }
    return sum[root];
}
char s[MAXN];
signed main() {
    while(gets(s)) {
        if(s[0]=='\0') break;
        add(s);
    }
    while(scanf("%s",s)!=EOF) {
        cout<<find(s)<<'\n';
    }
    return 0;
}

2.P2580 因而他错误的点名开始了

P2580 因而他错误的点名开始了

相关文章
相关标签/搜索