雙數組字典樹(Double Array Trie)


參考文獻

1.雙數組字典樹(DATrie)詳解及實現

2.小白詳解Trie樹

3.論文《基於雙數組Trie樹算法的字典改進和實現》

 

        DAT的基本內容介紹這里就不展開說了,從Trie過來的同學應該比較熟悉,Trie對內存的消耗比較大,DAT正是為了優化該問題而提出。此文重點說一下如何去理解DAT的base數組和check數組,希望能給諸位些幫助,DAT中定義base數組、check數組滿足的條件為:

        base[s] + c = t 

        check[t] = s    

這里s指轉移前的狀態,c指字符的編碼,t指轉移后的狀態,下面逐個理解這兩個公式表示的邏輯。

1. base[s] + c = t

       理解這個式子時,不能單純的從數組取值上去理解,比如直接將s當成base數組的下標,因為 s 和 t 都是狀態,而 c 是字符編碼,此時就會疑惑,為啥等式兩邊輸出類型都不一致呢?這到底是怎么個計算關系?如果有類似的疑惑,那么可以先摒棄這種的想法。  

       base數組維護的是Trie樹上節點的信息,這個公式想表達的意思是:狀態 s (也就是一個節點,Trie樹上每個節點都表示一個狀態,不理解的可以先了解一下狀態機的概念)接收一個字符 c 后,就得到狀態 t ,而並不是base數組中下標為 s 處的取值加上 c值 等於 t 值,所以說不能從僅僅從數值計算上理解這個公式。那么這個公式是如何用在DAT的創建中的呢?我們從如下幾個問題入手,來了解這個公式的作用。

       1.1  s、t 具體是什么意思?

       s、t 表示DAT上的一種狀態、base數組中的元素,實際上就是Trie上的一個個節點,這個節點上包含很多信息,創建過Trie樹的同學知道,Trie樹節點上一般包含:屬性值value(可以用來記錄詞性)、葉子節點標記flag(是否為詞語的未字符)、子節點數組(用來存儲子節點的信息)等等

      1.2  base數組中存儲的是什么?

       base數組維護的是各個狀態的信息,即數組中存儲的是各節點的信息,但是具體內容與1.1節中所說的Trie節點信息不一致,以參考文獻1中實現為例,每個狀態下包含的信息包括

      

 

      transferRatio:計算子節點在base數組中下標時的轉移基數,為了解決節點存儲位置沖突而引入的,初始值為0

      isLeaf:節點是否為詞語的葉子節點。該內容可選,也可以用其它方式表示葉子節點

      label:節點存儲的字符,可以理解為該節點是通過插入哪個字符得到的。該內容可選,也可以不要

      value:如果該節點為葉子節點,那么其對應的詞語在詞典中序號。該內容為可選,也可以不要

      1.3  base數組的下標表示什么意思 

        base數組的下標是基於字符的編碼做加減運算得到的數值。參照1.2節中的例子,創建DAT存儲詞語“中華”,先創建一個base數組,

       TrieNode base[10]     //假設存儲10個節點

 令根節點root = new TrieNode ,很自然的將根節點root存入base[0]。令 root. transferRatio=1 (這里設為1,也可以設個其他值,初始值隨便,保證base數組不溢出即可)

接着插入字符“中”,假如采用unicode編碼,那么“中”的碼值為20013,那么存儲 字符“中”的節點在base數組中的下標就為

0 + base[0].transferRatio + unicode("中")=0+1+20013=20014

令 base[20014].transferRatio=1    (這里設為1主要是為了和初始時的0區分開,表示base[20014]這個位置已經有節點占據了)

然后插入字符“華”,unicode('華')=21326,因為是節點“中”接收字符“華”,存儲 字符“華”的節點在base數組中的下標就為

20014+ base[20014].transferRatio + unicode('華')=20014+0+21326=41340,

令base[41340].transferRatio=1  ,原因同上。

      1.4  c值怎么計算

      c值得計算除了1.3中說的計算字符得unicode值,你還可以計算字符hash值得到,只要保證每個字符(中文、英文、日文等等文字的字符)有一個唯一且確定得編碼值即可。

 

       通過以上幾個問題的說明可知,DAT中每個狀態的下標是唯一的(解決位置沖突之后),可以用base數組的下標表示狀態,通過索引base數組的下標即可得到各個狀態的信息。此時,我們再來從數值計算的角度來理解這個公式,即狀態 s 的下標(還要加上偏移transferRatio)加上字符c的編碼值,等於狀態 t (由狀態s 接收字符c 得到)的下標。

 

2. check[t] = s    

      理解這個等式,先要從DAT的查詢邏輯說起。在Trie中,我們是怎么判斷詞語存在的?從根節點開始,依次查詢詞語中的每個字符,若各個字符均存在於當前節點的子節點中,則表明該詞語存在。如果在DAT上也按照這個邏輯來判斷詞語是否存在,則查詢過程是這樣的:還是以1.3節為例,查詢詞語 “中華” 是否存在。從根節點開始,首先查詢 “中”字,

0 + base[0].transferRatio + unicode("中")=0+1+20013=20014

若base[20014].transferRatio ≠ 0 ,則表明字符“中”存在。(判斷base數組中一個位置上是否有數據可以采用很多方式,這里采用參考文獻1中的實現方式,即判斷TrieNode.transferRatio是否非0)

接着查詢字符“華”,

20014+ base[20014].transferRatio + unicode('華')=20014+0+21326=41340

若base[20014].transferRatio ≠ 0,則表明“華”存在。那么這個邏輯在DAT上是否可靠呢?答案是不可靠,因為在DAT上節點轉移是通過在base數組上索引字符unicode碼值進行的,這就會存在以下的情況

unicode(A)  ≠  unicode(B)

unicode(A)+unicode(C) = unicode(B)+unicode(D)

其中 A、B、C、D指unicode編碼規范中收錄的某個字符,此種情況下的查詢邏輯就會出錯,所以在DAT中引入了check數組,在check數組中保存的是當前狀態的父狀態(即當前節點的父節點),還是以1.3節中詞語“中華”為例,以base數組的下標表示狀態,字符“中”所在節點的父節點為根節點,所以check[20014]=0;字符“華”所在節點的父節點下標為20014,即check[41340]=20014,這樣可以確定當前節點的父節點是哪一個,從而解決   unicode(A)+unicode(C) = unicode(B)+unicode(D) 帶來的問題。

         

           以上介紹的內容是基於參考文獻1中的簡單實現來說明的,實際上可以基於DAT的基本邏輯做很多種實現。

 

    

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM