亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

Java中實現雙數組Trie樹實例

2019-11-26 15:19:06
字體:
來源:轉載
供稿:網友

傳統的Trie實現簡單,但是占用的空間實在是難以接受,特別是當字符集不僅限于英文26個字符的時候,爆炸起來的空間根本無法接受。

雙數組Trie就是優化了空間的Trie樹,原理本文就不講了,請參考An Efficient Implementation of Trie Structures,本程序的編寫也是參考這篇論文的。

關于幾點論文沒有提及的細節和與論文不一一致的實現:

1.對于插入字符串,如果有一個字符串是另一個字符串的子串的話,我是將結束符也作為一條邊,產生一個新的結點,這個結點新節點的Base我置為0

所以一個字符串結束也有2中情況:一個是Base值為負,存儲剩余字符(可能只有一個結束符)到Tail數組;另一個是Base為0。

所以在查詢的時候要考慮一下這兩種情況

2.對于第一種沖突(論文中的Case 3),可能要將Tail中的字符串取出一部分,作為邊放到索引中。論文是使用將尾串左移的方式,我的方式直接修改Base值,而不是移動尾串。


下面是java實現的代碼,可以處理相同字符串插入,子串的插入等情況

復制代碼 代碼如下:

/*
 * Name:   Double Array Trie
 * Author: Yaguang Ding
 * Mail: dingyaguang117@gmail.com
 * Blog: blog.csdn.net/dingyaguang117
 * Date:   2012/5/21
 * Note: a word ends may be either of these two case:
 * 1. Base[cur_p] == pos  ( pos<0 and Tail[-pos] == 'END_CHAR' )
 * 2. Check[Base[cur_p] + Code('END_CHAR')] ==  cur_p
 */


import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;


public class DoubleArrayTrie {
 final char END_CHAR = '/0';
 final int DEFAULT_LEN = 1024;
 int Base[]  = new int [DEFAULT_LEN];
 int Check[] = new int [DEFAULT_LEN];
 char Tail[] = new char [DEFAULT_LEN];
 int Pos = 1;
 Map<Character ,Integer> CharMap = new HashMap<Character,Integer>();
 ArrayList<Character> CharList = new ArrayList<Character>();
 
 public DoubleArrayTrie()
 {
  Base[1] = 1;
  
  CharMap.put(END_CHAR,1);
  CharList.add(END_CHAR);
  CharList.add(END_CHAR);
  for(int i=0;i<26;++i)
  {
   CharMap.put((char)('a'+i),CharMap.size()+1);
   CharList.add((char)('a'+i));
  }
  
 }
 private void Extend_Array()
 {
  Base = Arrays.copyOf(Base, Base.length*2);
  Check = Arrays.copyOf(Check, Check.length*2);
 }
 
 private void Extend_Tail()
 {
  Tail = Arrays.copyOf(Tail, Tail.length*2);
 }
 
 private int GetCharCode(char c)
 {
  if (!CharMap.containsKey(c))
  {
   CharMap.put(c,CharMap.size()+1);
   CharList.add(c);
  }
  return CharMap.get(c);
 }
 private int CopyToTailArray(String s,int p)
 {
  int _Pos = Pos;
  while(s.length()-p+1 > Tail.length-Pos)
  {
   Extend_Tail();
  }
  for(int i=p; i<s.length();++i)
  {
   Tail[_Pos] = s.charAt(i);
   _Pos++;
  }
  return _Pos;
 }
 
 private int x_check(Integer []set)
 {
  for(int i=1; ; ++i)
  {
   boolean flag = true;
   for(int j=0;j<set.length;++j)
   {
    int cur_p = i+set[j];
    if(cur_p>= Base.length) Extend_Array();
    if(Base[cur_p]!= 0 || Check[cur_p]!= 0)
    {
     flag = false;
     break;
    }
   }
   if (flag) return i;
  }
 }
 
 private ArrayList<Integer> GetChildList(int p)
 {
  ArrayList<Integer> ret = new ArrayList<Integer>();
  for(int i=1; i<=CharMap.size();++i)
  {
   if(Base[p]+i >= Check.length) break;
   if(Check[Base[p]+i] == p)
   {
    ret.add(i);
   }
  }
  return ret;
 }
 
 private boolean TailContainString(int start,String s2)
 {
  for(int i=0;i<s2.length();++i)
  {
   if(s2.charAt(i) != Tail[i+start]) return false;
  }
  
  return true;
 }
 private boolean TailMatchString(int start,String s2)
 {
  s2 += END_CHAR;
  for(int i=0;i<s2.length();++i)
  {
   if(s2.charAt(i) != Tail[i+start]) return false;
  }
  return true;
 }
 
 
 public void Insert(String s) throws Exception
 {
  s += END_CHAR;
  
  int pre_p = 1;
  int cur_p;
  for(int i=0; i<s.length(); ++i)
  {
   //獲取狀態位置
   cur_p = Base[pre_p]+GetCharCode(s.charAt(i));
   //如果長度超過現有,拓展數組
   if (cur_p >= Base.length) Extend_Array();
   
   //空閑狀態
   if(Base[cur_p] == 0 && Check[cur_p] == 0)
   {
    Base[cur_p] = -Pos;
    Check[cur_p] = pre_p;
    Pos = CopyToTailArray(s,i+1);
    break;
   }else
   //已存在狀態
   if(Base[cur_p] > 0 && Check[cur_p] == pre_p)
   {
    pre_p = cur_p;
    continue;
   }else
   //沖突 1:遇到 Base[cur_p]小于0的,即遇到一個被壓縮存到Tail中的字符串
   if(Base[cur_p] < 0 && Check[cur_p] == pre_p)
   {
    int head = -Base[cur_p];
    
    if(s.charAt(i+1)== END_CHAR && Tail[head]==END_CHAR) //插入重復字符串
    {
     break;
    }
    
    //公共字母的情況,因為上一個判斷已經排除了結束符,所以一定是2個都不是結束符
    if (Tail[head] == s.charAt(i+1))
    {
     int avail_base = x_check(new Integer[]{GetCharCode(s.charAt(i+1))});
     Base[cur_p] = avail_base;
     
     Check[avail_base+GetCharCode(s.charAt(i+1))] = cur_p;
     Base[avail_base+GetCharCode(s.charAt(i+1))] = -(head+1);
     pre_p = cur_p;
     continue;
    }
    else
    {
     //2個字母不相同的情況,可能有一個為結束符
     int avail_base ;
     avail_base = x_check(new Integer[]{GetCharCode(s.charAt(i+1)),GetCharCode(Tail[head])});
     
     Base[cur_p] = avail_base;
     
     Check[avail_base+GetCharCode(Tail[head])] = cur_p;
     Check[avail_base+GetCharCode(s.charAt(i+1))] = cur_p;
     
     //Tail 為END_FLAG 的情況
     if(Tail[head] == END_CHAR)
      Base[avail_base+GetCharCode(Tail[head])] = 0;
     else
      Base[avail_base+GetCharCode(Tail[head])] = -(head+1);
     if(s.charAt(i+1) == END_CHAR)
      Base[avail_base+GetCharCode(s.charAt(i+1))] = 0;
     else
      Base[avail_base+GetCharCode(s.charAt(i+1))] = -Pos;
     
     Pos = CopyToTailArray(s,i+2);
     break;
    }
   }else
   //沖突2:當前結點已經被占用,需要調整pre的base
   if(Check[cur_p] != pre_p)
   {
    ArrayList<Integer> list1 = GetChildList(pre_p);
    int toBeAdjust;
    ArrayList<Integer> list = null;
    if(true)
    {
     toBeAdjust = pre_p;
     list = list1;
    }
    
    int origin_base = Base[toBeAdjust];
    list.add(GetCharCode(s.charAt(i)));
    int avail_base = x_check((Integer[])list.toArray(new Integer[list.size()]));
    list.remove(list.size()-1);
    
    Base[toBeAdjust] = avail_base;
    for(int j=0; j<list.size(); ++j)
    {
     //BUG
     int tmp1 = origin_base + list.get(j);
     int tmp2 = avail_base + list.get(j);
     
     Base[tmp2] = Base[tmp1];
     Check[tmp2] = Check[tmp1];
     
     //有后續
     if(Base[tmp1] > 0)
     {
      ArrayList<Integer> subsequence = GetChildList(tmp1);
      for(int k=0; k<subsequence.size(); ++k)
      {
       Check[Base[tmp1]+subsequence.get(k)] = tmp2;
      }
     }
     
     Base[tmp1] = 0;
     Check[tmp1] = 0;
    }
    
    //更新新的cur_p
    cur_p = Base[pre_p]+GetCharCode(s.charAt(i));
    
    if(s.charAt(i) == END_CHAR)
     Base[cur_p] = 0;
    else
     Base[cur_p] = -Pos;
    Check[cur_p] = pre_p;
    Pos = CopyToTailArray(s,i+1);
    break;
   }
  }
 }
 
 public boolean Exists(String word)
 {
  int pre_p = 1;
  int cur_p = 0;
  
  for(int i=0;i<word.length();++i)
  {
   cur_p = Base[pre_p]+GetCharCode(word.charAt(i));
   if(Check[cur_p] != pre_p) return false;
   if(Base[cur_p] < 0)
   {
    if(TailMatchString(-Base[cur_p],word.substring(i+1)))
     return true;
    return false;
   }
   pre_p = cur_p;
  }
  if(Check[Base[cur_p]+GetCharCode(END_CHAR)] == cur_p)
   return true;
  return false;
 }
 
 //內部函數,返回匹配單詞的最靠后的Base index,
 class FindStruct
 {
  int p;
  String prefix="";
 }
 private FindStruct Find(String word)
 {
  int pre_p = 1;
  int cur_p = 0;
  FindStruct fs = new FindStruct();
  for(int i=0;i<word.length();++i)
  {
   // BUG
   fs.prefix += word.charAt(i);
   cur_p = Base[pre_p]+GetCharCode(word.charAt(i));
   if(Check[cur_p] != pre_p)
   {
    fs.p = -1;
    return fs;
   }
   if(Base[cur_p] < 0)
   {
    if(TailContainString(-Base[cur_p],word.substring(i+1)))
    {
     fs.p = cur_p;
     return fs;
    }
    fs.p = -1;
    return fs;
   }
   pre_p = cur_p;
  }
  fs.p =  cur_p;
  return fs;
 }
 
 public ArrayList<String> GetAllChildWord(int index)
 {
  ArrayList<String> result = new ArrayList<String>();
  if(Base[index] == 0)
  {
   result.add("");
   return result;
  }
  if(Base[index] < 0)
  {
   String r="";
   for(int i=-Base[index];Tail[i]!=END_CHAR;++i)
   {
    r+= Tail[i];
   }
   result.add(r);
   return result;
  }
  for(int i=1;i<=CharMap.size();++i)
  {
   if(Check[Base[index]+i] == index)
   {
    for(String s:GetAllChildWord(Base[index]+i))
    {
     result.add(CharList.get(i)+s);
    }
    //result.addAll(GetAllChildWord(Base[index]+i));
   }
  }
  return result;
 }
 
 public ArrayList<String> FindAllWords(String word)
 {
  ArrayList<String> result = new ArrayList<String>();
  String prefix = "";
  FindStruct fs = Find(word);
  int p = fs.p;
  if (p == -1) return result;
  if(Base[p]<0)
  {
   String r="";
   for(int i=-Base[p];Tail[i]!=END_CHAR;++i)
   {
    r+= Tail[i];
   }
   result.add(fs.prefix+r);
   return result;
  }
  
  if(Base[p] > 0)
  {
   ArrayList<String> r =  GetAllChildWord(p);
   for(int i=0;i<r.size();++i)
   {
    r.set(i, fs.prefix+r.get(i));
   }
   return r;
  }
  
  return result;
 }
 
}

測試代碼:

復制代碼 代碼如下:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Scanner;

import javax.xml.crypto.Data;


public class Main {

 public static void main(String[] args) throws Exception {
  ArrayList<String> words = new ArrayList<String>();
  BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("E:/兔子的試驗學習中心[課內]/ACM大賽/ACM第四屆校賽/E命令提示/words3.dic")));
  String s;
  int num = 0;
  while((s=reader.readLine()) != null)
  {
   words.add(s);
   num ++;
  }
  DoubleArrayTrie dat = new DoubleArrayTrie();
  
  for(String word: words)
  {
   dat.Insert(word);
  }
  
  System.out.println(dat.Base.length);
  System.out.println(dat.Tail.length);
  
  Scanner sc = new Scanner(System.in);
  while(sc.hasNext())
  {
   String word = sc.next();
   System.out.println(dat.Exists(word));
   System.out.println(dat.FindAllWords(word));
  }
  
 }

}

下面是測試結果,構造6W英文單詞的DAT,大概需要20秒

我增長數組的時候是每次長度增加到2倍,初始1024

Base和Check數組的長度為131072

Tail的長度為262144

TTT1

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
米奇精品一区二区三区在线观看| 亚洲国产成人在线播放| 亚洲日本aⅴ片在线观看香蕉| 国内精品久久久久久中文字幕| 伊人久久男人天堂| 亚洲一区二区三区香蕉| 精品亚洲男同gayvideo网站| 隔壁老王国产在线精品| 亚洲国产欧美日韩精品| 91精品国产91久久| 亚洲色图35p| 欧美精品一二区| 国产区亚洲区欧美区| 久久人91精品久久久久久不卡| 中文字幕亚洲字幕| 亚洲免费一级电影| 亚洲国内精品视频| 亚洲色图第一页| 日韩av在线免费看| 一区二区三区四区精品| 国产精品久久一区| 91色琪琪电影亚洲精品久久| 在线观看国产精品日韩av| 日韩电影在线观看免费| 欧美老女人xx| 国产精品白丝jk喷水视频一区| 亚洲欧洲日产国码av系列天堂| 精品国产乱码久久久久久虫虫漫画| 中文字幕亚洲一区在线观看| 揄拍成人国产精品视频| 国产精品狠色婷| 欧美理论电影在线播放| 日韩av在线免费观看一区| 欧美日韩国产成人| 亚洲女人被黑人巨大进入al| 亚洲天堂色网站| 国产香蕉一区二区三区在线视频| 亚洲国产成人爱av在线播放| 国产精品福利在线观看网址| 欧美又大粗又爽又黄大片视频| 欧美不卡视频一区发布| 日韩视频免费在线观看| 欧美性xxxx极品高清hd直播| 亚洲综合国产精品| 亚洲精品99久久久久中文字幕| 国产99久久精品一区二区| 久久在线观看视频| 欧美黑人性生活视频| 亚洲网在线观看| 97精品视频在线观看| 欧美最猛性xxxxx免费| 91精品在线播放| 中文字幕欧美日韩精品| 日本免费在线精品| 国产成人精品电影| 性欧美长视频免费观看不卡| 91精品久久久久久久久久另类| 午夜精品免费视频| 欧美日韩国产精品| 亚洲欧美变态国产另类| 亚洲人成电影网站色| 国产一区深夜福利| 日韩午夜在线视频| 久久久伊人日本| 国产亚洲免费的视频看| 国产精品中文字幕在线| 一区二区在线视频播放| 国产精品国产三级国产aⅴ9色| 久久久在线观看| 日韩亚洲第一页| 亚洲精品久久久久久下一站| 影音先锋欧美精品| 美女av一区二区| 久久久久这里只有精品| 欧美激情在线观看视频| 欧美日韩免费在线| 91日本在线观看| 8x海外华人永久免费日韩内陆视频| 夜夜狂射影院欧美极品| 亚洲a级在线播放观看| 91网站在线免费观看| 日韩av片电影专区| 国模精品视频一区二区三区| 日韩成人中文字幕在线观看| 欧美精品videosex牲欧美| 视频一区视频二区国产精品| 亚洲精品理论电影| 日韩精品在线观看一区| 亚洲精品久久久久久久久久久| 国产91在线播放| 久久综合久久美利坚合众国| 精品国产一区二区三区久久狼黑人| 色樱桃影院亚洲精品影院| 国产91色在线播放| 岛国av一区二区三区| 欧美精品情趣视频| 久久躁狠狠躁夜夜爽| 中文字幕一精品亚洲无线一区| 狠狠综合久久av一区二区小说| 欧美色另类天堂2015| 永久免费看mv网站入口亚洲| 亚洲精品按摩视频| 欧美亚州一区二区三区| 97精品国产91久久久久久| 69av在线播放| 国产精品一区二区三区免费视频| 亚州av一区二区| 日本一欧美一欧美一亚洲视频| 成人黄色免费片| 欧美日韩在线视频一区| 久久久久久久影视| 亚洲嫩模很污视频| 九九精品在线观看| 日韩精品免费综合视频在线播放| 亚洲欧美中文另类| 欧美一乱一性一交一视频| 亚洲国产精品久久精品怡红院| 久久99国产精品久久久久久久久| 欧美国产日韩免费| 国产91精品黑色丝袜高跟鞋| 一区二区欧美激情| 91中文字幕一区| 91禁外国网站| 国产亚洲精品va在线观看| 亚洲精品久久久久| 欧美激情极品视频| 在线视频免费一区二区| 欧美激情精品久久久久久变态| 欧美激情一区二区三区成人| 国产精品久久国产精品99gif| 在线午夜精品自拍| 亚洲福利影片在线| 亚洲国产精品va在线观看黑人| 日韩色av导航| 爽爽爽爽爽爽爽成人免费观看| 亚洲福利影片在线| 中文字幕精品av| 成人激情视频在线播放| 日韩一区二区三区国产| 欧美视频国产精品| 亚洲欧美日韩图片| 日韩在线视频线视频免费网站| 久久99国产精品自在自在app| 日韩美女免费观看| 日韩高清有码在线| 久久久久久美女| 国产日韩在线视频| 日日骚av一区| 黄色一区二区三区| 久精品免费视频| 国产激情久久久久| 欧美激情综合色综合啪啪五月| 精品国产精品三级精品av网址| 欧美精品一区三区| 清纯唯美日韩制服另类| 久久久久国产精品免费网站| 欧美性猛交xxxx| 91精品国产免费久久久久久| 成人免费黄色网| 日韩av在线资源| 亚洲精品国精品久久99热一| 国外成人在线播放| 97在线观看视频| 97香蕉超级碰碰久久免费的优势|