zoukankan      html  css  js  c++  java
  • 【字符串算法】字典树Trie入门

    基本概念

    顾名思义,字典树(也叫前缀树)就是可以像字典那样来保存一些单词的集合。

    如图所示:

     (图片来自OIWiKi)

    设根节点的标号为$0$,然后其余结点依次编号;我们用数组来存每个节点的所有子节点

    更具体地,设数组$ch[MaxNode][SigmaSize]$,其中$MaxNode$表示最大可能的节点个数,$SigmaSize$是字符集合。$ch[i][j]$表示结点标号为$i$的结点 的 字母编号为$j$(比如说,j=字母-'a')的子节点的结点标号。如果$ch[i][j]$为$0$,则表示$i$没有字母编号为$j$的子节点。

    操作-插入

    插入的时候,直接从根节点开始遍历,根据要插入的字符串往下面走,如果碰到没有的字符就新插入结点就可以了。

    scanf("%s",s+1);
    //插入字符串 这颗trie根节点是从1开始的 
    int u=1,len=strlen(s+1);
    for(int j=1;j<=len;j++)
    {
        int c=s[j]-'a';
        if(!ch[u][c])//原本没有这个字符
            ch[u][c]=++tot;//插入这个字符
        u=ch[u][c]; 
    }

    操作-查询

    查询与操作是相似的,这里不再赘述。

    不过要注意,在字典树里面可以查找到的不一定就是单词,还有可能是单词的前缀。

    比如说字典树里面有一个单词为$apple$,查询$app$时在字典树里面会查到它,但是它只是一个前缀,不是单词。

    所以我们还需要再添加一个标记数组$tag[i]$表示节点标号为$i$的结点是否为一个单词的结尾,我们把这个节点叫做单词结点。显而易见,我们之前的那个插入就要改一下了。

    同样的,这个标记数组还可以干一些别的事情,比如在字符串被附了权值的情况下存这个字符串(以当前结点结尾的字符串)的权值。

    scanf("%s",s+1);
    int u=1,len=strlen(s+1);
    for(int j=1;j<=len;j++)
    {
        int c=s[j]-'a';
        u=ch[u][c];
        if(!u) break;//不存在 
    }
    if(tag[u]==0) ...  //不存在 

    操作-删除

    其实直接把单词结点的标记清掉就可以了。

    但是呢,也可以针对特殊情况特殊处理一下。

    分类讨论一下:

    1. 那么直接把整个单词全部删掉就可以了。(清$ch[][]$
    2. 如果被删除单词的那条链上某处有了分支,然后那个分支应该是有一支是表示被删除单词的,其他的都不是,那么删掉这一支就可以了(清$ch$

    其实还是有些麻烦,还不如直接清标记...

    例题 & 模板

    于是他错误的点名开始了

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 #include<queue>
     6 using namespace std;
     7 #define N 500005
     8 int ch[N][26];//结点i的为j+'a'子节点的节点标号
     9 char s[55];
    10 int n,m,tag[N],tot=1; 
    11 int main()
    12 {
    13     scanf("%d",&n);
    14     for(int i=1;i<=n;i++)
    15     {
    16         scanf("%s",s+1);
    17         //插入字符串 这颗trie根节点是从1开始的 
    18         int u=1,len=strlen(s+1);
    19         for(int j=1;j<=len;j++)
    20         {
    21             int c=s[j]-'a';
    22             if(!ch[u][c])//原本没有这个字符
    23                 ch[u][c]=++tot;//插入这个字符
    24             u=ch[u][c]; 
    25         }
    26         tag[u]=1;//标记这个结点是单词结点 
    27         //--- 
    28     }
    29     scanf("%d",&m);
    30     while(m--)
    31     {
    32         scanf("%s",s+1);
    33         int u=1,len=strlen(s+1);
    34         for(int j=1;j<=len;j++)
    35         {
    36             int c=s[j]-'a';
    37             u=ch[u][c];
    38             if(!u) break;//名字不存在 
    39         }
    40         if(tag[u]==1)
    41         {
    42             puts("OK");
    43             tag[u]=2;
    44         }
    45         else if(tag[u]==2)
    46             puts("REPEAT");
    47         else puts("WRONG");
    48     }
    49     return 0;
    50 }
    View Code

    后记

    做了例题之后,发现$Trie$干的事情$map$也能干,$Hash$也能干,不就是存个单词,查个单词嘛!

    别急,他还有很多用法...检索字符串只是最基本的操作而已...

  • 相关阅读:
    Constructor构造方法
    overload重载
    static关键字
    this关键字
    继承
    ORACLE数据库 常用命令和Sql常用语句
    常见单词
    L贪心基础
    J贪心
    K贪心
  • 原文地址:https://www.cnblogs.com/lyttt/p/12219609.html
Copyright © 2011-2022 走看看