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

WisKeyのLullaby

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

  C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
  12 Posts :: 0 Stories :: 23 Comments :: 0 Trackbacks

公告

“我該走哪條路?”
“這取決于你要去哪里?!?br> “我只想能到某個(gè)地方?!?br> “只要你走的夠遠(yuǎn),你始終能到達(dá)那個(gè)地方?!?br>

Home: huangwei.pro
E-Mail: sir.huangwei [at] gmail.com
09.6 畢業(yè)于杭州電子科技大學(xué)
進(jìn)入網(wǎng)易杭州研究院工作至今

常用鏈接

留言簿(1)

我參與的團(tuán)隊(duì)

搜索

  •  

積分與排名

  • 積分 - 51837
  • 排名 - 449

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

 

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中已經(jīng)十分普及,也是一種非常有效的索引結(jié)構(gòu),好處就不多說(shuō)了。

它的本質(zhì)就是一個(gè)確定的有限狀態(tài)自動(dòng)機(jī)(DFA),關(guān)于它的實(shí)現(xiàn)也是有好幾種,ACM中用的最多也是最容易實(shí)現(xiàn)的就是多路查找樹(shù)。

但是Trie最大的缺點(diǎn)就是占用空間過(guò)大,很容易爆內(nèi)存,當(dāng)然在ACM里對(duì)Trie樹(shù)也有相應(yīng)的優(yōu)化,如限定高度,對(duì)分支較少的節(jié)點(diǎn)使用非隨機(jī)訪問(wèn)的結(jié)構(gòu)(減少寬度),但這些都是犧牲部分查找效率換取的。

這里介紹一種實(shí)現(xiàn),Double-Array Trie(雙數(shù)組字典樹(shù)),其實(shí)它就是雙數(shù)組,跟樹(shù)結(jié)構(gòu)沒(méi)啥關(guān)系。他能在一定程度上減少內(nèi)存的浪費(fèi)。

兩個(gè)數(shù)組,一個(gè)是base[],另一個(gè)是check[]。設(shè)數(shù)組下標(biāo)為i ,如果base[i], check[i]均為0,表示該位置為空。如果base[i]為負(fù)值,表示該狀態(tài)為終止態(tài)(即詞語(yǔ))。check[i]表示該狀態(tài)的前一狀態(tài)。

定義 1. 對(duì)于輸入字符 c, 從狀態(tài) s 轉(zhuǎn)移到狀態(tài) t, 雙數(shù)組字典樹(shù)滿足如下條件:

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

定義1中,我們能得到查找算法,對(duì)于給定的狀態(tài) s 和輸入字符 c 

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

插入的操作,假設(shè)以某字符開(kāi)頭的 base 值為i,第二個(gè)字符的字符序列碼依次為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。

我們知道雙數(shù)組的實(shí)現(xiàn)方法是當(dāng)狀態(tài)有新轉(zhuǎn)移時(shí)才分配空間給新?tīng)顟B(tài),或可以表述為只分配需要轉(zhuǎn)移的狀態(tài)的空間。當(dāng)遇到無(wú)法滿足上述條件時(shí)再進(jìn)行調(diào)整,使得其 base 值滿足上述條件,這種調(diào)整只影響當(dāng)前節(jié)點(diǎn)下一層節(jié)點(diǎn)的重分配,因?yàn)樗泄?jié)點(diǎn)的地址分配是靠 base 數(shù)組指定的起始下標(biāo)所決定的。

我們先得到重分配算法:

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

如:有兩個(gè)單詞ac和da,先插入單詞ac,這時(shí)的 base 數(shù)組

插入單詞da的d時(shí),發(fā)現(xiàn)該地址已被c占用,我們進(jìn)行重分配

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

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

實(shí)際編碼過(guò)程中,DATrie代碼難度大過(guò)多路查找樹(shù),主要是狀態(tài)的表示不如樹(shù)結(jié)構(gòu)那樣的清晰,下標(biāo)很容易搞混掉。
有個(gè)地方需要注意的是,base值正數(shù)表示起始偏移量,負(fù)數(shù)表示該狀態(tài)為終止態(tài),所以在查找新base值時(shí),要保證查到的值是正數(shù)。
如:空Trie狀態(tài)下,插入d時(shí),因?yàn)榈谝粋€(gè)空地址是1,所以得到base=1-4=-3,這樣base正負(fù)的含義就被破壞了。

關(guān)于優(yōu)化:

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

    定義 2. 設(shè) 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]作為根節(jié)點(diǎn),所以初始化時(shí)就可以把base[0]排除在鏈表之外,而check[0]可以被作為鏈表的頭節(jié)點(diǎn)。

    設(shè)節(jié)點(diǎn)的狀態(tài)轉(zhuǎn)移集為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}

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

    重分配或刪除節(jié)點(diǎn)后,原先的地址被作廢,可以重新加入鏈表,這樣如果遇到原狀態(tài)轉(zhuǎn)移集的子集時(shí),就可以派上用場(chǎng)了。
    其實(shí)這部分的優(yōu)化就是使用了閑置信息域做成了鏈表,所以這部分的插入和刪除優(yōu)化原理就很容易理解了,時(shí)間復(fù)雜度為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. 數(shù)組長(zhǎng)度的壓縮
  4. 當(dāng)有節(jié)點(diǎn)刪除時(shí),我們不僅可以把它加回到鏈表中,還可以重新為最大非空節(jié)點(diǎn)的狀態(tài)重新確定base值,因?yàn)閯h除可能導(dǎo)致它的前面有空間容納下它的狀態(tài)轉(zhuǎn)移集。這樣我們可能又得以刪除一些空值狀態(tài),使得數(shù)組長(zhǎng)度有希望被壓縮。

  5. 字符后綴的壓縮
  6. 這個(gè)思想借鑒于后綴樹(shù),我們可以將沒(méi)有分支的后綴單獨(dú)存放,但這個(gè)結(jié)構(gòu)肯定獨(dú)立于DATrie,所以在這就不詳述了。詳情見(jiàn)[Aoe1989]。

整體而言,在ACM中,DATrie略高的編碼復(fù)雜度和過(guò)低的插入效率,應(yīng)用面不會(huì)太廣。但現(xiàn)實(shí)問(wèn)題中,詞庫(kù)大小一般比較穩(wěn)定,在離線算法也有很大的優(yōu)化余地,此時(shí)DATrie的空間優(yōu)勢(shì)就會(huì)比較明顯。畢竟Trie高效的檢索效率這一優(yōu)點(diǎn)是值得研究探討的。
這篇日志寫(xiě)的夠長(zhǎng)了,等有空再把DATrie測(cè)試報(bào)告給整理下吧。唉,發(fā)現(xiàn)自己語(yǔ)言組織能力越來(lái)越差了,寫(xiě)的連自己有時(shí)都看不下去,要多堅(jiān)持記下技術(shù)日志了~~

以下是只加入空地址查找優(yōu)化后的DATrie代碼,對(duì)于字符集表的映射結(jié)構(gòu)也是個(gè)需要持續(xù)討論的問(wèn)題,在這個(gè)代碼里只支持英文字母。

  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) 評(píng)論(1)  編輯 收藏 引用

Feedback

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

check[r[i]] = -r[i]+1 ; 1 <= i <= cm-1
這里應(yīng)該是
check[r[i]] = -r[i+1] ; 1 <= i <= cm-1  回復(fù)  更多評(píng)論
  


只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   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>
            你懂的视频一区二区| 久久成人免费网| 中文久久乱码一区二区| 久久久久九九九| 亚洲激情综合| 欧美11—12娇小xxxx| 国产麻豆精品久久一二三| 欧美成人69av| 日韩一级视频免费观看在线| 亚洲欧美视频一区二区三区| 国产日韩av一区二区| 亚洲国产精品一区二区www| 久久久午夜精品| 亚洲全部视频| 激情欧美丁香| 亚洲第一二三四五区| 国产精品日日摸夜夜摸av| 国产精品捆绑调教| 国产婷婷一区二区| 亚洲国产一区二区视频 | 午夜精品999| 欧美主播一区二区三区| 噜噜噜在线观看免费视频日韩| 欧美日韩一区免费| 怡红院精品视频| 亚洲一区区二区| 欧美xxx成人| 亚洲免费婷婷| 欧美日韩国产成人在线免费| 国产主播在线一区| 一区二区欧美精品| 亚洲影视综合| 欧美主播一区二区三区美女 久久精品人| 久久精品国产999大香线蕉| 亚洲大片在线观看| 久久精品视频在线播放| 欧美性大战久久久久| 亚洲国产美女精品久久久久∴| 亚洲综合久久久久| 欧美制服丝袜| 久久久.com| 亚洲精选视频免费看| 欧美一级久久久久久久大片| 久久精品国产清自在天天线| 久久尤物电影视频在线观看| 国产精品久久久久久久久搜平片| 国产亚洲欧洲一区高清在线观看| 国产日韩欧美在线视频观看| 欧美激情精品久久久久久大尺度| 亚洲一区二区三区欧美| 久久精品欧美日韩| 国产精品毛片a∨一区二区三区|国| 亚洲国产精品悠悠久久琪琪| 欧美在线资源| 亚洲欧美国产不卡| 国产精品久久久久9999| 中文一区二区| 亚洲精品国精品久久99热一| 免费欧美日韩| 亚洲青涩在线| 亚洲大胆美女视频| 欧美激情视频一区二区三区不卡| 91久久精品一区| 一本久久综合亚洲鲁鲁| 欧美与黑人午夜性猛交久久久| 国产精品久久久久久亚洲调教| 亚洲色在线视频| 一个色综合导航| 国产精品美女一区二区在线观看| 午夜欧美大片免费观看| 亚洲欧美日韩综合aⅴ视频| 国产精品一区二区黑丝| 久久精品成人欧美大片古装| 欧美一区视频在线| 在线观看福利一区| 亚洲国产成人不卡| 欧美午夜精品久久久久久浪潮| 亚洲色诱最新| 欧美中文在线免费| 亚洲日韩第九十九页| 亚洲美女免费视频| 国产欧美一区二区三区在线看蜜臀| 久久国产精品72免费观看| 久久性天堂网| 中国成人在线视频| 羞羞漫画18久久大片| 亚洲国产毛片完整版| 日韩午夜电影| 国产夜色精品一区二区av| 欧美国产日本| 国产精品久久久久毛片大屁完整版| 久久精品国产99国产精品澳门| 久久精品免费电影| 亚洲肉体裸体xxxx137| 亚洲午夜电影在线观看| 国产丝袜一区二区三区| 亚洲精品免费在线观看| 国产精品日韩一区二区三区| 亚洲黄色尤物视频| 欧美日韩国产页| 欧美日韩一区二区三区在线观看免| 免费成人高清视频| 欧美wwwwww| 欧美一级淫片播放口| 亚洲激情在线播放| 亚洲视频国产视频| 91久久国产综合久久蜜月精品 | 久久人人97超碰人人澡爱香蕉 | 亚洲伊人观看| 久久亚洲国产成人| 欧美一级在线亚洲天堂| 欧美韩日亚洲| 欧美freesex交免费视频| 国产精品成人播放| 亚洲电影毛片| 国产日本欧美一区二区三区| 亚洲国产91| 激情成人亚洲| 性欧美xxxx视频在线观看| 日韩一区二区精品在线观看| 久久这里只有| 久久一区亚洲| 国产色综合网| 午夜免费在线观看精品视频| 亚洲欧美国内爽妇网| 欧美日本一区二区三区| 欧美激情2020午夜免费观看| 伊人成人开心激情综合网| 欧美在线啊v| 久久精品国产欧美亚洲人人爽| 国产精品福利影院| 亚洲视频一起| 性欧美长视频| 亚洲精品免费在线| 麻豆久久婷婷| 国产亚洲一本大道中文在线| 亚洲午夜精品久久| 亚洲欧美韩国| 国产乱子伦一区二区三区国色天香| 一区二区三区导航| 亚洲女人av| 国产精品爱久久久久久久| 日韩午夜电影| 亚洲欧美日韩国产| 国产精品永久| 欧美自拍偷拍午夜视频| 久久激情五月婷婷| 好吊视频一区二区三区四区 | 在线观看一区二区精品视频| 先锋影音国产一区| 亚洲天堂网在线观看| 亚洲免费精彩视频| 噜噜噜噜噜久久久久久91| 精品成人免费| 亚洲日本中文字幕免费在线不卡| 欧美mv日韩mv国产网站| 噜噜噜噜噜久久久久久91| 亚洲国产一区二区a毛片| 国产精品天天看| 欧美日本一道本| 欧美国产日韩一区二区三区| 久久精品毛片| 久久九九国产精品怡红院| 亚洲一区二区三区精品动漫| 亚洲欧洲日本在线| 你懂的国产精品永久在线| 久久久久综合网| 久久国产精品电影| 亚洲激情电影中文字幕| 欧美一区二区免费视频| 日韩午夜激情av| 亚洲激情综合| 亚洲国产精品一区二区第四页av| 国模精品一区二区三区色天香| 国产精品自拍网站| 国产精品综合视频| 国产精品自拍在线| 国产精品亚洲人在线观看| 国产九九视频一区二区三区| 国产美女精品免费电影| 国产色产综合产在线视频| 国内精品久久久久久久果冻传媒 | 麻豆精品精品国产自在97香蕉| 久久激情一区| 另类天堂av| 欧美韩日高清| 国产精品v日韩精品| 国产精品热久久久久夜色精品三区| 国产精品高潮呻吟久久| 国产精品永久免费视频| 国产在线乱码一区二区三区| 一区免费在线| 亚洲欧洲日本国产| 亚洲手机视频| 久久国产精品免费一区| 免费试看一区| 亚洲精品美女久久7777777| 一本色道久久加勒比88综合| 亚洲欧美色婷婷| 久久久亚洲欧洲日产国码αv|