精华内容
下载资源
问答
  • 2016年夏天,花了不少时间认真研究过双数组trie树的相关理论,并花了一周时间将它实现了。关于代码,后面还会再增加一些解释信息。 双数组trie树相关资料: 博客:https://linux.thai.net/~thep/datrie/datrie....

    2016年夏天,花了不少时间认真研究过双数组trie树的相关理论,并花了一周时间将它实现了。关于代码,后面还会再增加一些解释信息。

    双数组trie树相关资料:

    基于双数组trie树的字符串匹配实现:

    https://github.com/choumin/double_array_trie

    展开全文
  • arrays.dic是一个双数组Trie树格式的词典,用文本的形式储存了索引,字串,base,check,status与词性。 一个直观的图示: index就是base数组中的下...

    http://www.hankcs.com/nlp/ansj-word-pairs-array-tire-tree-achieved-with-arrays-dic-dictionary-format.html

    arrays.dic是一个双数组Trie树格式的词典,用文本的形式储存了索引,字串,base,check,status与词性。

    一个直观的图示:

    index就是base数组中的下标。

    term是词的当前状态,不一定代表一个词,如“一举一”是“一举一动”的前缀。

    base是base数组的值。代表字串的当前状态,其实就是字串一路按base[tx] = base[t] + x查过来的值。比如base[一举一动] = base[一举一] + code(动)。特别地,如果字串长度为1的话(字符),那么base值就是字符的双字节码。

    check是check数组的值。check是用来验证这个词是从哪个状态转换过来的。比如是由转换过来的。base[105540] + 21160 = 126700.

    status是term的成词状态:1:继续 2:是个词语但是还可以继续 3:确定。参考

    nature是这个词以这些词性出现的频次。

    转载请注明:码农场 » Ansj分词双数组Trie树实现与arrays.dic词典格式

    转载于:https://www.cnblogs.com/DjangoBlog/p/4073062.html

    展开全文
  • 双数组Trie树实现笔记

    2009-05-20 15:58:54
    双数组的算法可以参照 、 An Efficient Implementation of Trie Structures 地址:[url]http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol22/issue9/spe777ja.pdf[/url] 该算法实际描述的是一个...
    双数组的算法可以参照 、
    An Efficient Implementation of Trie Structures
    地址:[url]http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol22/issue9/spe777ja.pdf[/url]
    该算法实际描述的是一个reduced trie
    上面讲的已经很详细了,只有插入操作的冲突解决第4个case 稍微麻烦些,简单记录一下

    在插入了 bachelor,jar,badge 三个单词后形成的trie树如图
    [img]http://www.iteye.com/upload/attachment/105611/9dc04653-7a5c-3652-a3e0-915a4ac73388.jpg[/img]

    这时有待插入单词 baby,那么根据定义,我们进行状态的转移

    BASE [l]+‘b’= BASE [ l]+3=l+3=4, and CHECK [4]=1
    BASE [4]+‘a’= BASE [4] +2=l+2=3, and CHECK [3]=4
    BASE [3]+‘b’= BASE [3 ]+3=l+3=4, and CHECK [4]=l != 3

    冲突发生,解决冲突的办法有2个

    a. 改变base[1]的值,使节点4变为 available状态,这样我们就可以指定 check[4]=3
    b. 改变base[3]的值,使base[3] + 'b'=t, and check[t]=0

    a,b具体选择哪个取决于 节点1,和节点3 谁的child更多,出于性能的考虑,我们当然改变那个child较少的节点,注意,节点3要算上新字符'b',所以 节点1有 [b,j] 2个child,节点3有[c,d,b] 3个child
    所以我们执行选择 a,改变 base[1]的值,最终结果如图
    [img]http://www.iteye.com/upload/attachment/105613/c2bd5b4a-6920-3004-99d9-b208d197303d.jpg[/img]
    具体base[1]的值选取,是从1开始,使 base[1] + base[1]的任意child 都可用,即
    check[val + each child of base1]==0

    本例base[1]最终选取值 为 4
    base[1]+'b' = 4+'b'= 4+3 =7 ,check[7]=0 available [color=red]完成 1 -> 7 节点关系[/color]
    base[1]+'j' = 4+ 'j'=4+11=15 ,check[15]=0 available

    这样 节点1的child就变为 7,15,从而使原来的 4,12变为空闲节点,通过以下步骤

    base[7]=base[4] 这样 节点7 + 'a' 能正确得到下一节点 3 [color=red]完成 7 -> 3 节点关系 [/color]
    check[7]=check[4] 这样 使得节点7可以指向(验证)前一节点是 1 [color=red]完成 7 -> 1 节点关系[/color]

    以上步骤分别完成了 1 -> 7,7 -> 1,7 -> 3 的关系建立

    可以看出我们还差一步就是 建立 3 ->7 的节点关系建立

    也就是说要使原来被替换掉的老节点的所有child都,改叫新的节点为父亲
    即使原来所有 check[?]=4 的 都变成 check[?]=7, (4是老节点,3的原来的父亲,7是新节点,现在3的新父亲)

    以上步骤完成后,我们成功的使节点 4 变为空闲节点,冲突解决,完整代码如下:


    #define BASE(n) (2*n >= bc_len ? 0 : bc[2*n])
    #define CHECK(n) (2*n+1 >=bc_len ? 0 : bc[2*n+1])
    #define ENCODE(n) ((unsigned char)n-95)
    #define MIN_CHILD 97
    #define MAX_CHILD 255

    //forward declaration
    static int bc_len;
    static int tail_len;
    static int tail_pos;
    //base and check,the double array
    static int *bc;
    //the tail array
    static unsigned char *tail;

    void bc_init();
    int bc_go_through(char *words,int insert);
    static int bc_change(int current,int s,char *list,char c);
    static char *list_childs(int node);
    static void write_tail(char *words,int tail_pos);
    static void separate(int s,char *words,int tail_pos);
    static int x_check(char *list);
    static void tail_insert(int s,char *temp,char *words);
    static void read_tail(char *temp,int pos);
    static void bc_insert(int s,char *words);
    static void bc_realloc();
    static void w_base(int n,int value);
    static void w_check(int n,int value);

    static void bc_realloc(){
    int *new;
    new=(int *)realloc(bc,sizeof(*new)*bc_len*2);
    if(!new){
    exit(1);
    }
    bc_len*=2;
    bc=new;
    }
    void bc_init(){
    bc_len=1024;
    tail_len=1024;
    bc=(int *)malloc(sizeof(*bc) * bc_len);
    if(!bc) exit(1);

    tail=(unsigned char *)malloc(sizeof(*tail)*tail_len);
    if(!tail) exit(1);

    w_base(1,1);
    tail_pos=1;
    }
    int bc_go_through(char *words,int insert){
    int i=0,s=1,t,len;
    char c,temp[100];
    words=strcat(words,"#");
    len=strlen(words);
    for(i=0;i<len;i++){

    if(words[i]=='#') break;

    c=ENCODE(words[i]);
    t=BASE(s)+c;
    if(CHECK(t)!=s){
    //case 1: check[t]==0,no collision,just make check[t]=s and save the rest in tail
    //case 2: collision occurs,it's the type 4 collision,described in "An Efficient Implements Of Trie Structures"
    if(insert) bc_insert(s,words+i);
    return 0;
    }
    if(BASE(t)<0) break; //case 1: found it,case 2: the rest in the tail and the rest of the words is different
    s=t;
    }
    //read tail and compare it with the rest of given words,then decide what to do next
    if(BASE(t)<0){
    read_tail(temp,BASE(t));
    if(!strcmp(temp,words+i+1)){
    return 1;
    }else{
    if(insert) tail_insert(t,temp,words+i+1); //type 3 collision
    return 0;
    }

    }
    return 0;
    }
    static void tail_insert(int s,char *temp,char *words){
    //solve type 3 collision
    char list[3];
    int old_tail_pos,len=0,i,t;
    old_tail_pos=abs(BASE(s));
    while(temp[len]==words[len]) len++;
    for(i=0;i<len;i++){
    list[0]=temp[i];
    list[1]='\0';

    w_base(s,x_check(list));
    t=BASE(s)+ENCODE(temp[i]);
    w_check(t,s);
    s=t;
    }
    list[0]=temp[len];
    list[1]=words[len];
    list[2]='\0';

    w_base(s,x_check(list));
    separate(s,temp+len,old_tail_pos);
    separate(s,words+len,tail_pos);
    tail_pos+=strlen(words+len);
    }
    static void separate(int s,char *words,int tail_pos){
    int t;
    t=BASE(s)+ENCODE(words[0]);
    w_check(t,s);
    w_base(t,(-1)*tail_pos);
    words++;
    write_tail(words,tail_pos);
    }
    static void write_tail(char *words,int tail_pos){
    unsigned char *new;
    int len;
    len=strlen(words);
    if(tail_pos+len>=tail_len){
    new=(unsigned char *)realloc(tail,sizeof(*new)*tail_len*2);
    if(!new) exit(1);
    tail_len*=2;
    tail=new;
    }
    memcpy(tail+tail_pos,words,len);
    }
    //choose a val,which make check[val + each element in list] == 0, actually val is base[n]
    static int x_check(char *list){
    int val=1,i=0,t;
    while(list[i]!='\0'){
    t=val+ENCODE(list[i++]);
    if(CHECK(t)!=0){
    val++;
    i=0;
    }
    }
    return val;
    }
    static void read_tail(char *temp,int pos){
    int i=0;
    pos=abs(pos);
    while(tail[pos+i] != '#') i++;
    memcpy(temp,tail+pos,i+1);
    temp[i+1]='\0';
    }
    static void bc_insert(int s,char *words){
    int t;
    char *s_childs,*t_childs;
    t=BASE(s)+ENCODE(words[0]);
    if(CHECK(t)!=0){
    s_childs=list_childs(s);
    t_childs=list_childs(CHECK(t));

    strlen(s_childs)+1>strlen(t_childs) ? bc_change(s,CHECK(t),t_childs,'\0') : bc_change(s,s,s_childs,words[0]);
    }
    separate(s,words,tail_pos);
    tail_pos+=(strlen(words)-1);
    }
    static int bc_change(int current,int s,char *list,char c){
    int old_base,new_base,i=0,j=0,new_node,old_node,len;
    char *old_childs;
    old_base=BASE(s);
    if(c!='\0'){
    len=strlen(list);
    list[len]=c;
    list[len+1]='\0';
    }
    new_base=x_check(list);
    w_base(s,new_base);
    while(list[i]!='\0'){
    new_node=new_base+ENCODE(list[i]);
    old_node=old_base+ENCODE(list[i]);

    w_base(new_node,BASE(old_node));
    w_check(new_node,s);

    if(BASE(old_node)>0){
    old_childs=list_childs(old_node);
    while(old_childs[j]!='\0'){
    w_check(BASE(new_node)+ENCODE(old_childs[j]),new_node);
    j++;
    }
    }
    w_base(old_node,0);
    w_check(old_node,0);
    i++;
    }
    return current;
    }
    static char *list_childs(int node){
    char *childs;
    int i,c,t;
    childs=(char *)malloc(sizeof(*childs)*(MAX_CHILD-MIN_CHILD+1));
    for(i=0,c=MIN_CHILD;c<MAX_CHILD;c++){
    t=BASE(node)+ENCODE(c);
    if(CHECK(t)==node) childs[i++]=c;
    }
    childs[i]='\0';
    return childs;
    }
    static void w_base(int n,int value){
    if(n>bc_len) bc_realloc();
    bc[2*n]=value;
    }
    static void w_check(int n,int value){
    if(n>bc_len) bc_realloc();
    bc[2*n+1]=value;
    }

    为了和文档中的例子保持一致方便调试,定义了这个宏 ENCODE(n) ((unsigned char)n-95)
    以及 MIN_CHILD,MAX_CHILD
    根据实际需要,需要改变这个宏的定义,调用实际的编码函数
    程序基本和文档中的保持一致,修改了一些小的地方

    写得太乱了,有兴趣的主要还是看文档吧,后续我也写个文字过滤试试
    目前还没想明白处理中英文时,实际的编码是否可以直接按ascii码处理,还要再想想
    展开全文
  • 主要介绍了Java中实现双数组Trie树实例,双数组Trie就是一种优化了空间的Trie树,本文给出了实现代码、测试代码和测试结果,需要的朋友可以参考下
  • https://www.hankcs.com/program/java/双数组trie树doublearraytriejava实现.html 双数组Trie树(DoubleArrayTrie)是一种空间复杂度低的Trie树,应用于字符区间大的语言(如中文、日文等)分词领域。 双数组...

    https://www.hankcs.com/program/java/双数组trie树doublearraytriejava实现.html

     

     

    双数组Trie树(DoubleArrayTrie)是一种空间复杂度低的Trie树,应用于字符区间大的语言(如中文、日文等)分词领域。

    双数组Trie (Double-Array Trie)结构由日本人JUN-ICHI AOE于1989年提出的,是Trie结构的压缩形式,仅用两个线性数组来表示Trie树,该结构有效结合了数字搜索树(Digital Search Tree)检索时间高效的特点和链式表示的Trie空间结构紧凑的特点。双数组Trie的本质是一个确定有限状态自动机(DFA),每个节点代表自动机的一个状态,根据变量不同,进行状态转移,当到达结束状态或无法转移时,完成一次查询操作。在双数组所有键中包含的字符之间的联系都是通过简单的数学加法运算表示,不仅提高了检索速度,而且省去了链式结构中使用的大量指针,节省了存储空间。

    ——《基于双数组Trie树算法的字典改进和实现》

    我看了几篇论文,发现中文里也就上面这篇质量最好,英文当属这篇双数组Trie的一种实现。不过我并不打算按论文的腔调摘抄理论,而是准备借助开源的 darts-java 写点代码分析与笔记,如果能帮到你,实属意外。

     

    darts-java 是对 Taku Kudo 桑的 C++ 版 Double Array Trie 的 Java 移植,代码精简,只有一个Java文件,十分优美。

    写一段测试代码

    
     
    1. package com.hankcs;
    2.  
    3. import darts.DoubleArrayTrie;
    4.  
    5. import java.io.*;
    6. import java.util.*;
    7.  
    8. /**
    9.  * @author hankcs
    10.  */
    11. public class Main
    12. {
    13.     public static void main(String[] args) throws IOException
    14.     {
    15.         BufferedReader reader = new BufferedReader(new FileReader("./data/small.dic"));
    16.         String line;
    17.         List<String> words = new ArrayList<String>();
    18.         Set<Character> charset = new HashSet<Character>();
    19.         while ((line = reader.readLine()) != null)
    20.         {
    21.             words.add(line);
    22.             // 制作一份码表debug
    23.             for (char c : line.toCharArray())
    24.             {
    25.                 charset.add(c);
    26.             }
    27.         }
    28.         reader.close();
    29.         // 这个字典如果要加入新词必须按字典序,参考下面的代码
    30. //        Collections.sort(words);
    31. //        BufferedWriter writer = new BufferedWriter(new FileWriter("./data/sorted.dic", false));
    32. //        for (String w : words)
    33. //        {
    34. //            writer.write(w);
    35. //            writer.newLine();
    36. //        }
    37.         System.out.println("字典词条:" + words.size());
    38.  
    39.         {
    40.             String infoCharsetValue = "";
    41.             String infoCharsetCode = "";
    42.             for (Character c : charset)
    43.             {
    44.                 infoCharsetValue += c.charValue() + "    ";
    45.                 infoCharsetCode += (int)c.charValue() + " ";
    46.             }
    47.             infoCharsetValue += '\n';
    48.             infoCharsetCode += '\n';
    49.             System.out.print(infoCharsetValue);
    50.             System.out.print(infoCharsetCode);
    51.         }
    52.  
    53.         DoubleArrayTrie dat = new DoubleArrayTrie();
    54.         System.out.println("是否错误: " + dat.build(words));
    55.         System.out.println(dat);
    56.         List<Integer> integerList = dat.commonPrefixSearch("一举成名天下知");
    57.         for (int index : integerList)
    58.         {
    59.             System.out.println(words.get(index));
    60.         }
    61.     }
    62. }

    其中small.dic是一个微型的词典:

    
     
    1. 一举
    2. 一举一动
    3. 一举成名
    4. 一举成名天下知
    5. 万能
    6. 万能胶

    输出:

    
     
    1. 字典词条:6
    2. 胶    名    动    知    下    成    举    一    能    天    万    
    3. 33014 21517 21160 30693 19979 25104 20030 19968 33021 22825 19975 
    4. 是否错误: 0
    5. 一举
    6. 一举成名
    7. 一举成名天下知

    Trie树的构造与双数组的构造

    双数组Trie树归根结底还是属于Trie树,所以免不了有一颗树的构造过程。不过这棵树并没有保存下来,而是边构造树边维护双数组,双数组的信息足以表示整棵树。比如对于上面的例子,首先建立一个空的root节点:

    Node{code=0, depth=0, left=0, right=6}

    其中code指的是字符的编码,在Java中是双字节,depth是深度,left及right表示这个节点在字典中的索引范围。

    比如:

    然后按照字典序插入所有的字串节点:

    其中绿色节点为空字符,代表从根节点到此节点的路径上的所有节点构成一个词,整个的构建顺序是:

     

    在darts-java中,使用了两个数组base和check来维护Trie树,它们的下标以及值都代表着一个确定的状态。base储存当前的状态以供状态转移使用,check验证字串是否由同一个状态转移而来并且当check为负的时候代表字串结束。(PS 双数组Tire树的具体实现有多种,有的实现将base为负作为状态的结束,大同小异。)

    假定有字符串状态s,当前字符串状态为t,假定t加了一个字符c就等于状态tc,加了一个字符x等于状态tx,那么有

    base[t] + c = base[tc]

    base[t] + x = base[tx]

    check[tc] = check[tx]

    可见,在单词“一举一动”中,虽然有两个“一”,但它们的前一个状态不同,所以对应的状态分别为“一”和“一举一”,在base数组中的下标不一样。

    在每个节点插入的过程中会修改这两个数组,具体说来:

    1、初始化root节点base[0] = 1; check[0] = 0;

    2、对于每一群兄弟节点,寻找一个begin值使得check[begin + a1…an]  == 0,也就是找到了n个空闲空间,a1…an是siblings中的n个节点对应的code。

    
     
    1.         int pos = siblings.get(0).code;
    2.         while (true)
    3.         {
    4.             pos++;
    5.             begin = pos - siblings.get(0).code; // 当前位置离第一个兄弟节点的距离
    6.             ……
    7.         }

    3、然后将这群兄弟节点的check设为check[begin + a1…an] = begin;很显然,叶子节点i的check[i]的值一定等于i,因为它是兄弟节点中的第一个,并且它的code为0。

    
     
    1. check[begin + siblings.get(i).code] = begin;

    4、接着对每个兄弟节点,如果它没有孩子,也就是上图除root外的绿色节点(叶子节点),令其base为负值;否则为该节点的子节点的插入位置(也就是begin值),同时插入子节点(迭代跳转到步骤2)。

    
     
    1.             if (fetch(siblings.get(i), new_siblings) == 0)  // 无子节点,也就是叶子节点,代表一个词的终止且不为其他词的前缀
    2.             {
    3.                 base[begin + siblings.get(i).code] = -siblings.get(i).left - 1;
    4.                 ……
    5.             }
    6.             else
    7.             {
    8.                 int h = insert(new_siblings);   // dfs
    9.                 base[begin + siblings.get(i).code] = h;
    10.             }

     

     

     

    这里给出这个例子的base check值以及码表,下表中×代表空

    
     
    1. 码表:
    2.    胶    名    动    知    下    成    举    一    能    天    万    
    3. 33014 21517 21160 30693 19979 25104 20030 19968 33021 22825 19975 
    4.  
    5. DoubleArrayTrie{
    6. char =      ×    一    万     ×    举     ×    动     ×     下    名    ×    知      ×     ×    能    一    天    成    胶
    7. i    =      0 19970 19977 20032 20033 21162 21164 21519 21520 21522 30695 30699 33023 33024 33028 40001 44345 45137 66038
    8. base =      1     2     6    -1 20032    -2 21162    -3     5 21519    -4 30695    -5    -6 33023     3  1540     4 33024
    9. check=      0     1     1 20032     2 21162     3 21519  1540     4 30695     5 33023 33024     6 20032 21519 20032 33023
    10. size=66039, allocSize=2097152, key=[一举, 一举一动, 一举成名, 一举成名天下知, 万能, 万能胶], keySize=6, progress=6, nextCheckPos=33024, error_=0}

    前缀查询

    定义当前状态p = base[0] = 1。按照字符串char的顺序walk:

    如果base[p] == check[base[p]] && base[base[p]] < 0则查到一个词;

    然后状态转移,增加一个字符  p = base[char[i-1]] + char[i] + 1 。加1是为了与null节点区分开。

     

    如果转移后base[char[i-1]] == check[base[char[i-1]] + char[i] + 1],那么下次p就从base[base[char[i-1]] + char[i] + 1]开始。

     

    结合例子看可能更清楚:

    
     
    1. 字典词条:6
    2. 胶    名    动    知    下    成    举    一    能    天    万    
    3. 33014 21517 21160 30693 19979 25104 20030 19968 33021 22825 19975 
    4. 是否错误: 0
    5. DoubleArrayTrie{
    6. char =     ×    一    万    ×    举    ×    动    ×    下    名    ×    知    ×    ×    能    一    天    成    胶
    7. i    =      0 19970 19977 20032 20033 21162 21164 21519 21520 21522 30695 30699 33023 33024 33028 40001 44345 45137 66038
    8. base =      1     2     6    -1 20032    -2 21162    -3     5 21519    -4 30695    -5    -6 33023     3  1540     4 33024
    9. check=      0     1     1 20032     2 21162     3 21519  1540     4 30695     5 33023 33024     6 20032 21519 20032 33023
    10. size=66039, allocSize=2097152, key=null, keySize=6, progress=6, nextCheckPos=33024, error_=0}
    11.  
    12. i       =      0     0     1     1     2     2     3     3     4     4     5     5     6     6    循环退出
    13. b       =      1     1     2     2 20032 20032     4     4 21519 21519  1540  1540     5     5    30695
    14.  
    15. p       =      1 19970     2 20033 20032 45137     4 21522 21519 44345  1540 21520     5 30699    30695
    16. base[p] =      1     2     0 20032    -1     4     0 21519    -3  1540     0     5     0 30695    -4
    17. check[p]=      0     1     0     2 20032 20032     0     4 21519 21519     0  1540     0     5    30695
    18. 一举
    19. 一举成名
    20. 一举成名天下知

    稍微解释下

    初始空 base[0] = 1, p = 1;

    转移 p = base[0] + {char[一] = 19968} + 1 = 1 + 19968 + 1 = 19970,                检查base[19970]!=0说明有“一”这个字符。

     而  base[base[19970]] = base[2] = 0 说明没遇到词尾

    转移 p = base[19970] + {char[举] = 20030} + 1 = 2 + 20030 + 1 = 20033,            检查base[20033]!=0说明有“举”这个字符。

     而  base[base[20033]] = base[20032] = -1 && base[20033] == check[20032] 说明遇到一个词尾,即查出“一举”

    转移 p = base[20033] + {char[成] = 25104} + 1 = 20032 + 25104+ 1 = 45137,         检查base[45137]!=0说明有“成”这个字符。

    ……

    基于双数组Trie树的Aho Corasick自动机

    双数组Trie树能高速O(n)完成单串匹配,并且内存消耗可控,然而软肋在于多模式匹配,如果要匹配多个模式串,必须先实现前缀查询,然后频繁截取文本后缀才可多匹配,这样一份文本要回退扫描多遍,性能极低。

    AC自动机能高速完成多模式匹配,然而具体实现聪明与否决定最终性能高低。大部分实现都是一个Map<Character, State>了事,无论是TreeMap的对数复杂度,还是HashMap的巨额空间复杂度与哈希函数的性能消耗,都会降低整体性能。

    如果能用双数组Trie树表达AC自动机,就能集合两者的优点,得到一种近乎完美的数据结构。具体实现请参考《Aho Corasick自动机结合DoubleArrayTrie极速多模式匹配》。

    展开全文
  • Trie树实现三----双数组trie树

    千次阅读 2013-09-30 16:19:38
    双数组trie结合了数组形式的转换表和链表形式的转换表的优点,不仅查询速度快,而且使用内存紧凑。 下面代码只是提供了Trie树的简单实现,没有考虑动态扩展base和check。具体实现过程的详解请查考An Efficient ...
  • 双数组TRIE树原理

    2019-01-16 15:37:25
    双数组TRIE树原理
  • 双数组trie树_互动百科 双数组trie树_互动百科双数组trie树开放分类: 搜索 算法编辑词条 分享 新知社 新浪微博 腾讯微博 人人网 QQ空间 网易微博 开心001 天涯 飞信空间 MSN 移动说客 双数组T...
  • 双数组Trie树

    2019-05-20 21:57:00
    搜索了很多解说双数组Trie树的博客, 很多上来就说双数组Trie树是一种有限状态自动机, 然后列出两个状态方程: 1. Base[t] + c.code = tc 2. Check[tc] = t 不敢说看懂了, 也不能说没收获, 始终有种迷迷糊糊的...
  • DoubleArrayTrie : DAT双数组Trie树

    千次阅读 2017-03-28 14:15:52
    码农场的《双数组Trie树(DoubleArrayTrie)Java实现》 外文《An Implementation of Double-Array Trie》DoubleArrayTrie 双数组Tire树是Tire树的升级版,Tire取自英文Retrieval中的一部分,即检索树,又称作
  • 原文地址:Java实现双数组Trie树(DoubleArrayTrie,DAT) 传统的Trie实现简单,但是占用的空间实在是难以接受,特别是当字符集不仅限于英文26个字符的时候,爆炸起来的空间根本无法接受。 双数组Trie就是优化了...
  • Trie树 原理 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,能在常数时间O(len)内实现插入和查询操作,是一种以...
  • 双数组trie树

    2019-03-29 14:56:18
    Trie树的数组实现原理 Posted on 2009-04-04 | 0 Trie(Retrieval Tree)又称前缀树,可以用来保存多个字符串,并且非常便于查找。在trie中查找一个字符串的时间只取决于组成该串的字符数,与树的节点数无关。因此,它...
  • 关于本源码更详细的解释说明,请参见:http://blog.csdn.net/lemon_tree12138/article/details/49281865

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 623
精华内容 249
关键字:

双数组trie树实现