字典树(trie tree)

概念相关

(因为前面期末考试的原因很久没有更了,也没有时间写题目了,暑假因为要集训和多校赛的话,会更的比较频繁,逃~)顾名思义,字典树,就是由一个个字符组成的一棵树。我们从根节点出发,在我们面临下一个单词的输入的时候,我们去新建一个节点存放当前的字符序列,ch[p][c]表示的是当前节点是上一个字符的下一个字符,所以这里我们应该会有感觉,我们在插入和查找字符串的时候,都是通过从根点沿着trie一直走,当我们走到这个字符串的末尾的时候,打上标记就代表这个字符串是存在于这棵树里面的。关于我们查找的次数我们同样可以进行处理,只需要这个点上面打上标记可以了。下面是字典树的结构示意图。在应用方面,我们可以用字典树来进行字串的统计,检索查询,对字符串集合进行字典序的排列等,操作性十分的高。
字典树示意图

字典树的优缺点

我们掌握了一个数据结构我们当然要了解他的作用和优缺点。首先我们来关注一下字典树操作的时间复杂度,插入和查找都是线性的时间,这个复杂度十分优秀了,这里我们要提一下它和hash的区别,hash的话查询和比较操作是常数级别的复杂度,但是我们知道字符hash的话,还是取决于hash函数的好坏,并且hash函数或多或少都是会发生hash碰撞的,而字典树的结构则具有唯一性,当然了,我们既然说到这里,字典树的唯一性是什么带来的哪?没错,是他开的巨大的数组,所以来说字典树其实采用了一种以时间换空间的思想。

代码实现

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int N=500005;
struct trie {
    int ch[N][26],cnt;
    bool exist [N];
    
    void insert (char *s,int len) {
       int p=0;
       for (int i=0;i<len;i++) {
           int c=s[i]-'a';
           if (!ch[p][c]) ch[p][c]=++cnt;
           p=ch[p][c];
       }
        exist[p]=1;
    }
    bool find (char *s,int len) {
       int p=0;
       for (int i=0;i<len;i++) {
           int c=s[i]-'a';
           if (!ch[p][c]) return 0;
           p=ch[p][c];
       }
       return exist[p];
    } 
};

字典树的简单应用

给你n个学生的名字,我进行m次点名,第一次点到的输出OK,重复点到的输出RE,不存在的学生输出Never。

思路

这个我们很容易想,我们读入字符串同时把他丢进trie tree里面,然后我们点名的时候去查找这个字符串是否存在就可以了,点过一次后记得把tag标志打成2,这样在下次点名的时候我们就可以做出相对应的输出了。

代码实现

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include<vector>
#include <functional>
#define x first
#define y second 
using namespace std;
typedef long long ll;
const int N=500010;
char s[60];
int n,m,ch[N][26],cnt,tag[N];
int main() {
    cin>>n;
    for (int i=1;i<=n;i++) {
       scanf ("%s",s+1);
       int u=1;
       for (int i=1;s[i];i++) {
          int c=s[i]-'a';
          if (!ch[u][c]) ch[u][c]=++cnt;   //没有就新建节点
          u=ch[u][c];
       }
       tag[u]=1;
    }
    cin>>m;
    for (int i=1;i<=m;i++) {
       scanf ("%s",s+1);
       int u=1;
       for (int j=1;s[j];j++) {
          int c=s[j]-'a';
          u=ch[u][c];        //沿着trie搜索,直到没有下一个根点为止
          if (!u) break;
       }
      if (tag[u]==1)  {
         tag[u]=2;
         puts("OK");
      }
      else if (tag[u]==2) puts("RE");
      else puts("Never");
    }
   return 0; 
}
原文地址:https://www.cnblogs.com/hhlya/p/13223683.html