青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

WisKeyのLullaby

huangwei.pro 『我失去了一只臂膀』「就睜開了一只眼睛」

  C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
  12 Posts :: 0 Stories :: 23 Comments :: 0 Trackbacks

公告

“我該走哪條路?”
“這取決于你要去哪里。”
“我只想能到某個地方。”
“只要你走的夠遠,你始終能到達那個地方。”

Home: huangwei.pro
E-Mail: sir.huangwei [at] gmail.com
09.6 畢業于杭州電子科技大學
進入網易杭州研究院工作至今

常用鏈接

留言簿(1)

我參與的團隊

搜索

  •  

積分與排名

  • 積分 - 51837
  • 排名 - 449

最新評論

閱讀排行榜

評論排行榜

 

http://blog.huang-wei.com/2010/07/15/double-array-trie%ef%bc%88%e5%8f%8c%e6%95%b0%e7%bb%84%e5%ad%97%e5%85%b8%e6%a0%91%ef%bc%89/


Trie在ACM中已經十分普及,也是一種非常有效的索引結構,好處就不多說了。

它的本質就是一個確定的有限狀態自動機(DFA),關于它的實現也是有好幾種,ACM中用的最多也是最容易實現的就是多路查找樹。

但是Trie最大的缺點就是占用空間過大,很容易爆內存,當然在ACM里對Trie樹也有相應的優化,如限定高度,對分支較少的節點使用非隨機訪問的結構(減少寬度),但這些都是犧牲部分查找效率換取的。

這里介紹一種實現,Double-Array Trie(雙數組字典樹),其實它就是雙數組,跟樹結構沒啥關系。他能在一定程度上減少內存的浪費。

兩個數組,一個是base[],另一個是check[]。設數組下標為i ,如果base[i], check[i]均為0,表示該位置為空。如果base[i]為負值,表示該狀態為終止態(即詞語)。check[i]表示該狀態的前一狀態。

定義 1. 對于輸入字符 c, 從狀態 s 轉移到狀態 t, 雙數組字典樹滿足如下條件:

check[base[s] + c] = s
base[s] + c = t 

定義1中,我們能得到查找算法,對于給定的狀態 s 和輸入字符 c 

t := base[s] + c;
if check[t] = s then
    next state := t
else
    fail
endif

插入的操作,假設以某字符開頭的 base 值為i,第二個字符的字符序列碼依次為c1, c2, c3…cn,則肯定要滿足base[i+c1], check[i+c1], base[i+c2], check[i+c2], base[i+c3], check[i+c3]…base[i+cn], check[i+cn]均為0。

我們知道雙數組的實現方法是當狀態有新轉移時才分配空間給新狀態,或可以表述為只分配需要轉移的狀態的空間。當遇到無法滿足上述條件時再進行調整,使得其 base 值滿足上述條件,這種調整只影響當前節點下一層節點的重分配,因為所有節點的地址分配是靠 base 數組指定的起始下標所決定的。

我們先得到重分配算法:

Procedure Relocate(s : state; b : base_index)
{ Move base for state s to a new place beginning at b }
begin
    foreach input character c for the state s
    { i.e. foreach c such that check[base[s] + c]] = s }
    begin
        check[b + c] := s;     { mark owner }
        base[b + c] := base[base[s] + c];     { copy data }
        { the node base[s] + c is to be moved to b + c;
          Hence, for any i for which check[i] = base[s] + c, update check[i] to b + c }
        foreach input character d for the node base[s] + c
        begin
            check[base[base[s] + c] + d] := b + c
        end;
        check[base[s] + c] := none     { free the cell }
    end;
    base[s] := b
end

如:有兩個單詞ac和da,先插入單詞ac,這時的 base 數組

插入單詞da的d時,發現該地址已被c占用,我們進行重分配

從上圖可見a和d的位置重新分配了, base 值從0變成了1。

假設,Tire里有n個節點,字符集大小為m,則DATrie的空間大小是n+cmc是依賴于Trie稀疏程度的一個系數。而多路查找樹的空間大小是nm
注意,這里的復雜度都是按離線算法(offline algorithm)計算的,即處理時已經得到整個詞庫。在線算法(online algorithm)的空間復雜度還和單詞出現的順序有關,越有序的單詞順序空間占用越小。
查找算法的復雜度和被查找的字符串長度相關的,這個復雜度和多路查找樹是一樣的。
插入算法中,如果出現重分配的情況,我們要附加上掃描子節點的時間復雜度,還有新base值確定的算法復雜度。假如這兒我們都是用暴力算法(for循環掃描),那插入算法時間復雜度是O(nm + cm2)。。

實際編碼過程中,DATrie代碼難度大過多路查找樹,主要是狀態的表示不如樹結構那樣的清晰,下標很容易搞混掉。
有個地方需要注意的是,base值正數表示起始偏移量,負數表示該狀態為終止態,所以在查找新base值時,要保證查到的值是正數。
如:空Trie狀態下,插入d時,因為第一個空地址是1,所以得到base=1-4=-3,這樣base正負的含義就被破壞了。

關于優化:

  1. 查找空地址優化
  2. base[i], check[i]均為0,表示該位置為空。我們可以把這部分給利用起來,全為0的標記所包含的信息實在太少了。我們利用basecheck數組組成一個雙向鏈表。

    定義 2. 設 r1, r2, … ,rcm 為空閑地址有序序列,則我們的雙向鏈表可定義為 :

    check[0] = -r[1]
    check[r[i]] = -r[i+1] 1 <= i <= cm-1
    check[r[cm]] = 0
    base[0] = -r[cm]
    base[r[1]] = 0
    base[r[i+1]] = -r[i] ; 1 <= i <= cm-1

    由于我們把base[0]作為根節點,所以初始化時就可以把base[0]排除在鏈表之外,而check[0]可以被作為鏈表的頭節點。

    設節點的狀態轉移集為P = {c1, c2, …, cp},依靠鏈表我們能得到新的空地址查找算法:

    {find least free cell s such that s > c[1]}
    s := -check[0];
    while s <> 0 and s <= c[1do
        s := -check[s]
    end;
    if s = 0 then return FAIL;  {or reserve some additional space}
     
    {continue searching for the row, given that s matches c[1]}
    while s <> 0 do
        i := 2;
        while i <= p and check[s + c[i] - c[1]] < 0 do
            i := i + 1
        end;
        if i = p + 1 then return s - c[1];  {all cells required are free, so return it}
        s := -check[-s]
    end;
    return FAIL;  {or reserve some additional space}

    優化后的空地址查找算法時間復雜度為O(cm2),而重分配算法的時間復雜度為O(m2),則總的時間復雜度為O(cm2 + m2) = O(cm2)。

    重分配或刪除節點后,原先的地址被作廢,可以重新加入鏈表,這樣如果遇到原狀態轉移集的子集時,就可以派上用場了。
    其實這部分的優化就是使用了閑置信息域做成了鏈表,所以這部分的插入和刪除優化原理就很容易理解了,時間復雜度為O(cm)。

    t := -check[0];
    while check[t] <> 0 and t < s do
        t := -check[t]
    end;
    {t now points to the cell after s' place}
    check[s] := -t;
    check[-base[t]] := -s;
    base[s] := base[t];
    base[t] := -s;
  3. 數組長度的壓縮
  4. 當有節點刪除時,我們不僅可以把它加回到鏈表中,還可以重新為最大非空節點的狀態重新確定base值,因為刪除可能導致它的前面有空間容納下它的狀態轉移集。這樣我們可能又得以刪除一些空值狀態,使得數組長度有希望被壓縮。

  5. 字符后綴的壓縮
  6. 這個思想借鑒于后綴樹,我們可以將沒有分支的后綴單獨存放,但這個結構肯定獨立于DATrie,所以在這就不詳述了。詳情見[Aoe1989]

整體而言,在ACM中,DATrie略高的編碼復雜度和過低的插入效率,應用面不會太廣。但現實問題中,詞庫大小一般比較穩定,在離線算法也有很大的優化余地,此時DATrie的空間優勢就會比較明顯。畢竟Trie高效的檢索效率這一優點是值得研究探討的。
這篇日志寫的夠長了,等有空再把DATrie測試報告給整理下吧。唉,發現自己語言組織能力越來越差了,寫的連自己有時都看不下去,要多堅持記下技術日志了~~

以下是只加入空地址查找優化后的DATrie代碼,對于字符集表的映射結構也是個需要持續討論的問題,在這個代碼里只支持英文字母。

  1#define ALPHASIZE                30
  2#define MAX                        10000
  3#define ALPHAID(x)                (1+x-'a')
  4#define IDALPHA(x)                (x-1+'a')
  5#define EMPTY(x)                (basei[x] < 0 && check[x] < 0)
  6#define DELETE_FREE_NODE(x)        check[-basei[x]] = check[x]; \
  7                                basei[-check[x]] = basei[x]; \
  8                                maxsize = max(maxsize, x)
  9#define ADD_FREE_NODE(x)        basei[x] = MAX; \
 10                                check[x] = MAX; \
 11                                abnodes ++
 12class DATire
 13{
 14public:
 15    void init() {
 16        // double circular linked list (except 0)
 17        for (int i = 1; i < MAX; i ++{
 18            check[i] = -(i+1);
 19            basei[i] = -(i-1);
 20        }

 21        basei[1= 0// so check[0] can be updated
 22        check[MAX-1= 1;
 23        // basei[0] is root-index
 24        // check[0] point to first free cell
 25        basei[0= 0;
 26        check[0= -1;
 27        // stat
 28        diffwords = 0;
 29        maxsize = 0;
 30        nodes = 1;
 31        abnodes = 0;
 32    }

 33    void print(int s, char* buf, int d) {
 34        if (basei[s] < 0)
 35            puts(buf);
 36        int si = abs(basei[s]);
 37        for (int i = si+1; i <= si + ALPHASIZE; i ++{
 38            if (check[i] == s) {
 39                buf[d] = IDALPHA(i-si); buf[d+1= '\0';
 40                print(i, buf, d+1);
 41                buf[d] = '\0';
 42            }

 43        }

 44    }

 45    bool insert(string word) {
 46        int s = 0, t;
 47        for (int i = 0; i < word.length(); i ++{
 48            char ch = word[i];
 49            t = abs(basei[s]) + ALPHAID(ch);
 50            if (s == check[t])
 51                s = t;
 52            else if (EMPTY(t)) {
 53                DELETE_FREE_NODE(t);
 54                basei[t] = t;
 55                check[t] = s;
 56                s = t;
 57                nodes ++;
 58            }

 59            else {
 60                int newb = findb(s, ALPHAID(ch));
 61                if (newb == -1)
 62                    return false;
 63                else {
 64                    relocate(s, newb);
 65                    i --;
 66                }

 67            }

 68        }

 69        if (basei[s] > 0)
 70            diffwords ++;
 71        basei[s] = -abs(basei[s]);
 72        return true;
 73    }

 74    bool find(string word) {
 75        int s = 0, t;
 76        int i;
 77        for (i = 0; i < word.length(); i ++{
 78            char ch = word[i];
 79            t = abs(basei[s]) + ALPHAID(ch);
 80            if (s == check[t])
 81                s = t;
 82            else
 83                break;
 84        }

 85        return (i == word.length() && basei[s] < 0);
 86    }

 87protected:
 88    int findb(int s, int newc) {
 89        ns = 0;
 90        int i, j;
 91        int si = abs(basei[s]);
 92        for (i = si+1; i <= si + ALPHASIZE; i ++{
 93            if (check[i] == s)
 94                sonc[ns ++= i - si;
 95        }

 96        sonc[ns ++= newc;
 97        int minson = min(sonc[0], newc);
 98        // i < si, the new place must be after old place
 99        // i < minson, the negative base value has other meaning
100        for (i = -check[0]; i != 0 && (i < si || i < minson); i = -check[i]) ;
101        for (; i != 0; i = -check[i]) {
102            for (j = 0; j < ns; j ++{
103                if (! EMPTY(i + sonc[j] - minson))
104                    break;
105            }

106            if (j == ns) {
107                ns --;
108                assert(i - minson >= 0);
109                return i - minson;
110            }

111        }

112        return -1;
113    }

114    void relocate(int s, int b) {
115        for (int i = ns-1; i >= 0; i --{
116            int news = b + sonc[i];
117            int olds = abs(basei[s]) + sonc[i];
118            DELETE_FREE_NODE(news);
119            check[news] = s;
120            basei[news] = basei[olds];
121            int isi = abs(basei[olds]);
122            for (int j = isi+1; j <= isi + ALPHASIZE; j ++{
123                if (check[j] == olds)
124                    check[j] = news;
125            }

126            ADD_FREE_NODE(olds);
127        }

128        basei[s] = (basei[s] < 0 ? -1 : 1* b;
129    }

130protected:
131    int basei[MAX];
132    int check[MAX];
133    // helper
134    int sonc[ALPHASIZE];
135    int ns;
136public:
137    // stat
138    int maxsize;    // used memory size
139    int nodes;        // trie nodes
140    int abnodes;    // abandoned trie nodes
141    int diffwords;    // diff words
142                    // free nodes = maxsize-nodes-abnodes
143}
;
posted on 2010-07-23 08:51 威士忌 閱讀(7522) 評論(1)  編輯 收藏 引用

Feedback

# re: Double-Array Trie(雙數組字典樹) 2011-08-29 10:43 仗劍

check[r[i]] = -r[i]+1 ; 1 <= i <= cm-1
這里應該是
check[r[i]] = -r[i+1] ; 1 <= i <= cm-1  回復  更多評論
  


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲成色777777女色窝| 欧美精品偷拍| 久久精品噜噜噜成人av农村| 牛牛影视久久网| 国精产品99永久一区一区| 日韩视频在线播放| 久久香蕉国产线看观看av| 亚洲最新视频在线播放| 欧美www视频在线观看| 国产小视频国产精品| 一区二区欧美在线观看| 亚洲高清一区二| 欧美在线观看视频一区二区| 国产精品久久久久天堂| 夜夜爽99久久国产综合精品女不卡| 久久九九久久九九| 亚洲在线视频免费观看| 国产精品进线69影院| 中文一区字幕| 99re8这里有精品热视频免费 | 亚洲综合另类| 亚洲国产高清在线| 99精品久久免费看蜜臀剧情介绍| 蜜桃伊人久久| 久久国产免费| 欧美另类一区| 欧美va亚洲va国产综合| 亚洲高清久久网| 欧美阿v一级看视频| 久久资源在线| 韩国一区二区在线观看| 一区二区成人精品| 日韩视频中文| 亚洲国产欧美国产综合一区 | 日韩视频中文字幕| 亚洲精品极品| 国产精品久久网| 性做久久久久久久久| 午夜精品久久久久久99热| 这里是久久伊人| 国产午夜亚洲精品理论片色戒| 久久精品五月婷婷| 久久在线精品| 亚洲乱码精品一二三四区日韩在线 | 久久免费视频在线观看| 亚洲国产精品第一区二区| 欧美极品一区| 欧美一区1区三区3区公司| 久久成人亚洲| 亚洲三级毛片| 国产精品99久久不卡二区| 国产麻豆精品在线观看| 麻豆精品一区二区av白丝在线| 欧美成人黑人xx视频免费观看| 中文久久精品| 久久九九有精品国产23| 夜夜嗨av一区二区三区四区| 午夜精品www| 久久久久久久久综合| 久久青青草综合| 亚洲国产精品一区二区www| 欧美日韩精品在线| 欧美高清视频一区| 亚洲国产成人精品久久| 亚洲综合好骚| 国产精品青草综合久久久久99 | 一本色道久久综合亚洲精品按摩| 一本久道久久综合狠狠爱| 韩日精品在线| 99re国产精品| 亚洲国产欧美国产综合一区| 一本一道久久综合狠狠老精东影业| 国内精品久久久久久久97牛牛| 91久久精品美女| 国产一区二区三区最好精华液| 亚洲国产第一| 韩国一区二区三区美女美女秀| 一本大道久久a久久综合婷婷| 国产精品亚洲激情| 欧美激情在线免费观看| 伊人久久大香线蕉综合热线| 亚洲国产专区| ●精品国产综合乱码久久久久| 一区二区三区欧美激情| 国产一区二区三区四区三区四| 欧美黄色网络| 亚洲精品国产精品乱码不99| 激情综合自拍| 激情久久五月天| 欧美在线观看你懂的| 欧美sm视频| 亚洲视频在线一区观看| 日韩午夜在线播放| 免费欧美视频| 免费成人黄色| 国产在线精品自拍| 亚洲欧美国产精品va在线观看| 亚洲网站在线观看| 欧美日韩一区在线| 亚洲精品久久久久久下一站| 亚洲高清色综合| 久久伊人亚洲| 开元免费观看欧美电视剧网站| 一区二区三区在线不卡| 欧美在线观看天堂一区二区三区| 欧美主播一区二区三区| 国产亚洲欧美日韩美女| 欧美一区二区三区免费视频 | 亚洲日本理论电影| 亚洲人成在线影院| 老鸭窝毛片一区二区三区| 免费观看久久久4p| 最新日韩在线| 欧美久久电影| 99精品视频免费观看| 亚洲免费视频观看| 国产精品永久入口久久久| 亚洲精品久久久久久下一站| 久久一二三区| 亚洲国产日韩欧美一区二区三区| 亚洲日本成人| 欧美日韩伦理在线| 一区二区三区你懂的| 久久国产精品一区二区三区四区 | 麻豆国产精品va在线观看不卡| 国语自产在线不卡| 久久资源在线| 亚洲欧洲综合| 亚洲欧美在线一区二区| 国产亚洲在线观看| 久久噜噜噜精品国产亚洲综合| 亚洲国产成人av| 一区二区三区欧美在线| 国产伦理精品不卡| 久久精品一区二区三区不卡牛牛| 欧美福利在线观看| 一区二区激情视频| 欧美日韩一区二区视频在线| 国产精品欧美日韩久久| 久久成人精品一区二区三区| 欧美国产第二页| 亚洲精品一区久久久久久| 欧美日韩综合一区| 欧美国产先锋| 亚洲一区二区三区精品动漫| 欧美成黄导航| 亚洲线精品一区二区三区八戒| 欧美日韩免费视频| 欧美专区一区二区三区| 亚洲国产美国国产综合一区二区| 午夜精品亚洲一区二区三区嫩草| 伊人夜夜躁av伊人久久| 欧美性猛片xxxx免费看久爱| 久久精品国产91精品亚洲| 亚洲影院色无极综合| 国内精品嫩模av私拍在线观看 | 国产精品免费看| 欧美精品色一区二区三区| 午夜精品福利视频| 亚洲精品国产精品国自产在线 | 国产亚洲精品aa午夜观看| 女人天堂亚洲aⅴ在线观看| 亚洲欧美资源在线| 夜夜嗨av色一区二区不卡| 麻豆精品在线视频| 亚洲宅男天堂在线观看无病毒| 国产一区二区三区黄| 欧美激情精品久久久六区热门 | 欧美激情在线观看| 亚洲男女自偷自拍| 国产精品99久久久久久宅男| 亚洲精选一区二区| 亚洲精品孕妇| 亚洲精品一区二区三区不| 欧美成人在线免费观看| 欧美一区精品| 亚洲精品国产品国语在线app| 国产午夜精品久久久久久免费视| 欧美日韩国产小视频在线观看| 美女国产精品| 久久夜色精品国产噜噜av| 亚洲一区尤物| 亚洲一区二区三区四区中文| 欧美黄色视屏| 欧美二区乱c少妇| 欧美va亚洲va日韩∨a综合色| 久久天天综合| 蜜桃av噜噜一区| 欧美伊人久久大香线蕉综合69| 亚洲一区二区在线免费观看| 亚洲日本欧美日韩高观看| 国内精品久久久久久久果冻传媒| 国产日韩一区二区三区在线| 国产美女精品人人做人人爽| 国产精品视屏| 黑丝一区二区| 久久中文字幕导航| 欧美国产欧美综合 | 99av国产精品欲麻豆| 亚洲天堂av高清|