SlideShare a Scribd company logo
1 of 46
Download to read offline
ダブル配列の実装方法
ダブル配列(Double Array)とは	
•  トライ木の実装方法の一つ
•  トライ木とは?
   •  大語彙で前方一致検索を高速に行うことが可能なデータ構造
   •  例えば形態素解析、かな漢字変換で利用される
   •  どこが単語の始まりでどこが単語の終わりかわからない時に有用	

ハッシュテーブルを使ってルックアップする方法の場合
  単語境界がわからないので一文字づつ文字数を増やしてルックアップするしかない
  最後までいったら開始点を1文字ずらして再度ルックアップ O(n^2) (入力文字数がn)
	
  わ た し の な ま え は な か の で す 。	



        ・・・
ダブル配列(Double Array)とは	
•  トライ木の実装方法の一つ
•  トライ木とは?
   •  大語彙で前方一致検索を高速に行うことが可能なデータ構造
   •  例えば形態素解析、かな漢字変換で利用される
   •  どこが単語の始まりでどこが単語の終わりかわからない時に有用	

正規表現を用いる方法
  語彙数が少ない場合はこれでもそれほど遅くはない
  現実は何十万語を使ってのルックアップ
$reg = /(和|輪綿|腸|私|わたし|野|の|名前|なまえ|名|中|野|仲|那賀|那加| ・・・
 ・・・ 
 ・・・
 ・・・
                                      ・・・|巣|す|酢)/;
入力文字数をn, 語彙数をm, 語彙の平均文字数をkとするとO(nmk)
トライを用いればO(nk)での計算が可能!
トライ木の中身	
 •  じゃぁトライ木はどうして効率的なのか?
    •  結局のところトライは決定性オートマトンの一種
    •  正規表現も中身はオートマトンを実装している	
              正規表現 (ab#|abc#|abdef#|acde#|ade#)が生成するオートマトン
              失敗したらバックトラックしてsノードから再度検索(実装による)	
語彙(#は終端文字)           b	
   #	
ab#
abc#          a	
abdef#               b	
   c	
   #	
acde#         a	
bde#	
        a	
    b	
   d	
   e	
   f	
   #	
        S	
              a	
                     c	
   d	
   e	
   #	
              b	

                     d	
   e	
   #
トライ木の構築するオートマトン	
 •  Prefixが共通するノードについてはまとめあげて、ノード数を
    少なくできる
 •  遷移に失敗したらその時点で該当の候補がないことが確定
   (再度開始ノードから探索しなくてもよい)	
              トライ木が構築するオートマトン	
語彙(#は終端文字)
ab#
                          #	
abc#
                    b	
   c	
    #	
abdef#
acde#         a	
                           d	
bde#	
                           e	
   f	
   #	
        S	

                    c	
   d	
    e	
   #	
              b	

                    d	
   e	
    #
トライ木の実装方法	
   遷移リストを用いた実装方法	
      状態遷移表を用いた実装方法	

•  各ノードで遷移先ノードのポインタを   •  各状態ごとに、ラベルに対応
 保持                     する遷移先ノード番号を保持	
struct node {                 a	
   b	
   c	
   d	
   列:
                                                      ラベル	
  node *to;             1	
   2	
   ×	
   4	
   ×	
  char *labels;         2	
   ×	
   ×	
   3	
   ×	
}                       3	
   ×	
   ×	
   ×	
   ◎	
                        4	
   ◎	
   ×	
   ×	
   ×	
問題点
                       行:ノード番号	
遷移先ノードを線形探索して          問題点
調べるので遷移が遅い	
           遷移のない部分についても情報を保持
                       する必要があるためメモリを大量に消
                       費する
Double Arrayとは	
•  状態遷移表を2つの配列(BASE, CHECK)に圧縮
状態遷移表で重要なのは以下の2種類
1.  遷移があるかないかという情報(CHECKで管理)
2.  遷移先のノード番号がどこかという情報(BASEで管理)

      a	
   b	
   c	
   d	
   BASE配列	
1	
   2	
   ×	
   4	
   ×	
2	
   ×	
   ×	
   3	
   ×	
   CHECK配列	
3	
   ×	
   ×	
   ×	
   ◎	
4	
   ◎	
   ×	
   ×	
   ×	


状態遷移表の高速性を維持したままで、現実的なメモリ使用量での
探索が可能!
Double Arrayを使ったノード遷移アルゴリズム	
    1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る
    2.  入力文字を文字コード表を用いて数字(以下cとする)に変換
        (実装上はunsigned char, 終端記号はヌル文字を用いることが多い)
    3.  次の状態番号(以下tとする)を下記の計算式で計算
           t = base[s] + c
    4.  CHECK[t]が元の状態番号sと等しいか確認する
        一致すれば遷移成功
                                                                       文字コード表	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
例) 現在の状態番号を6 入力文字をaとする	
                                               1	
   2	
   3	
   4	
   5	
   6	
インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     0	
   0	
   0	
   0	
   0	
   8	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      0	
   0	
   0	
   0	
   0	
   0	
   0	
   6	
   2	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使ったノード遷移アルゴリズム	
    1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る
    2.  入力文字を文字コード表を用いて数字(以下cとする)に変換
        (実装上はunsigned char, 終端記号はヌル文字を用いることが多い)
    3.  次の状態番号(以下tとする)を下記の計算式で計算
           t = base[s] + c
    4.  CHECK[t]が元の状態番号sと等しいか確認する
        一致すれば遷移成功
                                                                        文字コード表	
                                                                        a	
   b	
   c	
   d	
   e	
   #	
例) 現在の状態番号を6 入力文字をaとする	
                                                1	
   2	
   3	
   4	
   5	
   6	
インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
    7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     0	
   0	
   0	
   0	
   0	
   8	
    0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      0	
   0	
   0	
   0	
   0	
    0	
   0	
   6	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
                                               t = BASE[6] + 1 = 8 + 1 = 9
Double Arrayを使ったノード遷移アルゴリズム	
    1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る
    2.  入力文字を文字コード表を用いて数字(以下cとする)に変換
        (実装上はunsigned char, 終端記号はヌル文字を用いることが多い)
    3.  次の状態番号(以下tとする)を下記の計算式で計算
           t = base[s] + c
    4.  CHECK[t]が元の状態番号sと等しいか確認する
        一致すれば遷移成功
                                                                        文字コード表	
                                                                        a	
   b	
   c	
   d	
   e	
   #	
例) 現在の状態番号を6 入力文字をaとする	
                                                1	
   2	
   3	
   4	
   5	
   6	
インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
    7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     0	
   0	
   0	
   0	
   0	
   8	
    0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      0	
   0	
   0	
   0	
   0	
    0	
   0	
   6	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
                                               t = BASE[6] + 1 = 8 + 1 = 9
Double Arrayを使ったノード遷移アルゴリズム	
    1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る
    2.  入力文字を文字コード表を用いて数字(以下cとする)に変換
        (実装上はunsigned char, 終端記号はヌル文字を用いることが多い)
    3.  次の状態番号(以下tとする)を下記の計算式で計算
           t = base[s] + c
    4.  CHECK[t]が元の状態番号sと等しいか確認する
        一致すれば遷移成功
                                                                        文字コード表	
                                                                        a	
   b	
   c	
   d	
   e	
   #	
例) 現在の状態番号を6 入力文字をaとする	
                                 CHECK[9] =1	
 2	
 3	
 4	
 5	
 6	
                                                                     6なので
インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
    7	
   8	
 9	
 遷移成功!	
                                                              10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     0	
   0	
   0	
   0	
   0	
   8	
    0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      0	
   0	
   0	
   0	
   0	
    0	
   0	
   6	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
                                               t = BASE[6] + 1 = 8 + 1 = 9
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                       文字コード表	
例) 「abd#」で検索	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	




インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                         文字コード表N(c)	
例) 「abd#」で検索	
                                                                         a	
   b	
   c	
   d	
   e	
   #	
                                                                         1	
   2	
   3	
   4	
   5	
   6	
           BASE[1] + N(a) = 1 + 1 = 2	



インデックス	
     1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
       1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
        1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                       文字コード表N(c)	
例) 「abd#」で検索	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
           一致!	



インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                       文字コード表N(c)	
例) 「abd#」で検索	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
           BASE[2] + N(b) = 3 + 2 = 5	



インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                       文字コード表N(c)	
例) 「abd#」で検索	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
                       一致!	



インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                        文字コード表N(c)	
例) 「abd#」で検索	
                                                                        a	
   b	
   c	
   d	
   e	
   #	
                                                                        1	
   2	
   3	
   4	
   5	
   6	
                       BASE[5] + N(d) = 2 + 4 = 6	



インデックス	
   1	
   2	
    3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
    0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
    0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                       文字コード表N(c)	
例) 「abd#」で検索	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
                                    一致!	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                       文字コード表N(c)	
例) 「abd#」で検索	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
                             BASE[6] + N(#) = 2 + 6 = 8	



インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                        文字コード表N(c)	
例) 「abd#」で検索	
                                                                        a	
   b	
   c	
   d	
   e	
   #	
                                                                        1	
   2	
   3	
   4	
   5	
   6	

                                               一致!	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
    7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
    0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
    0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayを使った検索アルゴリズム	
    1.  現在の状態を1にセット
    2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う
    3.  遷移に失敗した段階で検索打ち切り. 失敗を返す
    4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で
        あれば検索成功 成功を返す

                                                                       文字コード表N(c)	
例) 「abd#」で検索	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	

                                     BASE[8] <= 0なので遷移なし 検索成功!	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   0	
   2	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   0	
   2	
   5	
   0	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
Double Arrayの構築方法	
    1.  二つの配列は全て0で初期化する
    2.  語彙を一つ一つ順次挿入していく(挿入アルゴリズムは後述)
    3.  全ての語彙を挿入し終わった時点で構築終了

    実用上、語彙は挿入前に全てsortしておくと、再配置が少なくなるため高速に
    構築可能となる
    また、静的構築の場合、より高速に構築する手法もある。



インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入	
    1.     まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する
           (検索に成功した場合は既に語彙があるため挿入の必要なし)
    2.     遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する
           使用済みノード(CHECK値>0)なら衝突処理を行う
    3.     残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し,
           以後残りの文字列について2 => 3 を繰り返す

    例) 既に「ab#」という語彙が追加されている時に、                                         文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
  「ab#」が追加された状態のDouble Array	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   0	
   0	
   0	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入	
    1.     まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する
           (検索に成功した場合は既に語彙があるため挿入の必要なし)
    2.     遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する
           使用済みノード(CHECK値>0)なら衝突処理を行う
    3.     残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し,
           以後残りの文字列について2 => 3 を繰り返す

    例) 既に「ab#」という語彙が追加されている時に、                                         文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                 cの文字で遷移しようとした時に失敗                                     1	
   2	
   3	
   4	
   5	
   6	
                 そのときの状態番号は「3」	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   0	
   0	
   0	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入	
    1.     まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する
           (検索に成功した場合は既に語彙があるため挿入の必要なし)
    2.     遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する
           使用済みノード(CHECK値>0)なら衝突処理を行う
    3.     残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し,
           以後残りの文字列について2 => 3 を繰り返す

    例) 既に「ab#」という語彙が追加されている時に、                                         文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                 現在のBASE値から次の状態を計算
                 t = BASE[3] + N(c) = 1 + 3 = 4	
                      1	
   2	
   3	
   4	
   5	
   6	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   0	
   0	
   0	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(CHECKの設定)	
    1.     まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する
           (検索に成功した場合は既に語彙があるため挿入の必要なし)
    2.     遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する
           使用済みノード(CHECK値>0)なら衝突処理を行う
    3.     残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し,
           以後残りの文字列について2 => 3 を繰り返す

    例) 既に「ab#」という語彙が追加されている時に、                                         文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                 CHECK[4] <= 0より未使用ノードなので
                 CHECK[4] = 3を設定                                       1	
   2	
   3	
   4	
   5	
   6	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(BASEの設定)	
    例) 既に「ab#」という語彙が追加されている時に、                                          文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする
                                                                        a	
   b	
   c	
   d	
   e	
   #	
    BASE値は、下記の手順で求める                1	
 2	
 3	
                                           4	
   5	
   6	
    (入力 現在の状態番号, 遷移文字)
    1.  BASE値の初期値を1に設定
    2.  現在のBASE値で遷移先ノードを計算
        遷移先ノードのCHECK値を確認して、未使用ノードなら3へ
        使用済みノードであればBASE値をインクリメントして再度2を実行
    3.  BASE値をBASE[現在の状態番号]に設定
    4.  CHECK[遷移先ノード]に現在の状態番号を設定
                               1 + N(#) = 7	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
    7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   0	
   0	
   0	
    0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
    3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(BASEの設定)	
    例) 既に「ab#」という語彙が追加されている時に、                                       文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする
                                                                     a	
   b	
   c	
   d	
   e	
   #	
    BASE値は、下記の手順で求める                                1	
 2	
 3	
 4	
 5	
 6	
    (入力 現在の状態番号, 遷移文字)
    1.  BASE値の初期値を1に設定
    2.  現在のBASE値で遷移先ノードを計算
        遷移先ノードのCHECK値を確認して、未使用ノードなら3へ
        使用済みノードであればBASE値をインクリメントして再度2を実行
    3.  BASE値をBASE[現在の状態番号]に設定
    4.  CHECK[遷移先ノード]に現在の状態番号を設定
                               CHECK[7] > 0のため
                               使用済み要素
                               BASE値=1は不適
インデックス	
  1	
 2	
 3	
 4	
 5	
 6	
 7	
 8	
 9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
   1	
   1	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
    1	
   2	
   3	
   0	
   0	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(BASEの設定)	
    例) 既に「ab#」という語彙が追加されている時に、                                          文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする
                                                                        a	
   b	
   c	
   d	
   e	
   #	
    BASE値は、下記の手順で求める                1	
 2	
 3	
                                           4	
   5	
   6	
    (入力 現在の状態番号, 遷移文字)
    1.  BASE値の初期値を1に設定
    2.  現在のBASE値で遷移先ノードを計算
        遷移先ノードのCHECK値を確認して、未使用ノードなら3へ
        使用済みノードであればBASE値をインクリメントして再度2を実行
    3.  BASE値をBASE[現在の状態番号]に設定
    4.  CHECK[遷移先ノード]に現在の状態番号を設定
                               2 + N(#) = 8	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
    7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   0	
   0	
   0	
    0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
    3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(BASEの設定)	
    例) 既に「ab#」という語彙が追加されている時に、                                       文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする
                                                                     a	
   b	
   c	
   d	
   e	
   #	
    BASE値は、下記の手順で求める                                1	
 2	
 3	
 4	
 5	
 6	
    (入力 現在の状態番号, 遷移文字)
    1.  BASE値の初期値を1に設定
    2.  現在のBASE値で遷移先ノードを計算
        遷移先ノードのCHECK値を確認して、未使用ノードなら3へ
        使用済みノードであればBASE値をインクリメントして再度2を実行
    3.  BASE値をBASE[現在の状態番号]に設定
    4.  CHECK[遷移先ノード]に現在の状態番号を設定
                                   CHECK[8] <= 0のため
                                   未使用ノード
                                   BASE値=2はOK
インデックス	
  1	
 2	
 3	
 4	
 5	
 6	
 7	
 8	
 9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
   1	
   1	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
    1	
   2	
   3	
   0	
   0	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(BASEの設定)	
    例) 既に「ab#」という語彙が追加されている時に、                                         文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする
                                                                       a	
   b	
   c	
   d	
   e	
   #	
    BASE値は、下記の手順で求める                1	
 2	
 3	
                                          4	
   5	
   6	
    (入力 現在の状態番号, 遷移文字)
    1.  BASE値の初期値を1に設定
    2.  現在のBASE値で遷移先ノードを計算
        遷移先ノードのCHECK値を確認して、未使用ノードなら3へ
        使用済みノードであればBASE値をインクリメントして再度2を実行
    3.  BASE値をBASE[現在の状態番号]に設定
    4.  CHECK[遷移先ノード]に現在の状態番号を設定

                  BASE[4] = 2を設定	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(BASEの設定)	
    例) 既に「ab#」という語彙が追加されている時に、                                          文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする
                                                                        a	
   b	
   c	
   d	
   e	
   #	
    BASE値は、下記の手順で求める                1	
 2	
 3	
                                           4	
   5	
   6	
    (入力 現在の状態番号, 遷移文字)
    1.  BASE値の初期値を1に設定
    2.  現在のBASE値で遷移先ノードを計算
        遷移先ノードのCHECK値を確認して、未使用ノードなら3へ
        使用済みノードであればBASE値をインクリメントして再度2を実行
    3.  BASE値をBASE[現在の状態番号]に設定
    4.  CHECK[遷移先ノード]に現在の状態番号を設定

                             CHECK[BASE[4]+6] = CHECK[8] = 4 を設定	

インデックス	
   1	
   2	
   3	
    4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
    2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
    3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(完了)	
    例) 既に「ab#」という語彙が追加されている時に、                                         文字コード表N(c)	
    新たに「abc#」という語彙を追加するとする
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
    「abc#」全ての遷移のBASE, CHECK値の挿入が
    完了したので挿入操作終了




インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突の場合)	
    •  次は挿入時に衝突がある場合を考える	



    例) 現在のDouble Arrayに「ac#」を挿入する場合

    基本的な手順は先述の場合と同じ
                                                                       文字コード表N(c)	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
            cの文字で遷移しようとした時に失敗                                          1	
   2	
   3	
   4	
   5	
   6	
            そのときの状態番号は「2」	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突の場合)	


    例) 現在のDouble Arrayに「ac#」を挿入する場合

    基本的な手順は先述の場合と同じ
                                                                       文字コード表N(c)	
           現在のBASE値から遷移先を計算
                                                                       a	
   b	
   c	
   d	
   e	
   #	
           t = BASE[2] + N(c) = 1 + 3 = 4	
                                                                       1	
   2	
   3	
   4	
   5	
   6	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突の場合)	


    例) 現在のDouble Arrayに「ac#」を挿入する場合

    基本的な手順は先述の場合と同じ
                                                                       文字コード表N(c)	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                         CHECK[4] > 0のため                 1	
 2	
 3	
 4	
 5	
 6	
                         使用済みノード
                         BASE[2]の再設定が必要となる
インデックス	
   1	
   2	
   3	
 4	
 5	
 6	
 7	
 8	
 9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)
    衝突処理
    1.  現在のノード番号から遷移している全てのラベル(文字)を取得
    2.  全てのラベルが遷移可能なBASE値及び上記ラベルの新しい遷移先を求める
    3.  上記ラベルについて、それぞれ古い遷移先のBASE値を新しい遷移先にコピー
    4.  古い遷移先のさらに遷移される先のCHECK値も新しい遷移先に更新
    5.  各ラベルの古い遷移先のノードを未使用状態に初期化
                                                                       文字コード表N(c)	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                                                                       1	
   2	
   3	
   4	
   5	
   6	
                 現在注目中のノード	

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)

    遷移ラベル(文字)の取得
    これは「CHECK[i] = 現在のノード番号」となる
    ノードを線形探索すればいい
    下記の図の場合「3」が該当する

                                                                         文字コード表N(c)	
                                                                         a	
   b	
   c	
   d	
   e	
   #	
                                                                         1	
   2	
   3	
   4	
   5	
   6	
                   b	

インデックス	
   1	
   2	
     3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   1	
     1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
     2	
   3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)
    BASE値の決定
    新しいBASE[2]の値として「b」「c」の2文字が遷移可能な
    BASE値を探す
    手順については先述と同様 複数の遷移ラベルで確認する必要があることに注意

    結果、新しいBASE値3が求まる
    今までのBASE[2] = 1を一時変数に保存し、
    BASE[2] = 3を設定する
                                                                         文字コード表N(c)	

             b	
                                                         a	
   b	
   c	
   d	
   e	
   #	
      1	
                                                                1	
   2	
   3	
   4	
   5	
   6	

インデックス	
    1	
    2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
      1	
    3	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
        1	
   2	
   3	
   0	
   0	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)
    遷移先CHECK値の更新
    「b」「c」の新しい遷移先でCHECK値を設定する
    「b」の新しい遷移先は5
    「c」の遷移先は6



                                                                          文字コード表N(c)	
        b	
                                                               a	
   b	
   c	
   d	
   e	
   #	
  1	
                                                                          1	
   2	
   3	
   4	
   5	
   6	
                          b	
         c	

インデックス	
      1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
        1	
   3	
   1	
   2	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
         1	
   2	
   3	
   2	
   2	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理 辻褄合わせ)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)
    元遷移先のBASE値コピー
    「b」の元遷移先「3」のBASE値を, 新しい遷移先「5」のBASE値ににコピー




                                                                          文字コード表N(c)	
                                                                          a	
   b	
   c	
   d	
   e	
   #	
        b	
                                                               1	
   2	
   3	
   4	
   5	
   6	
  1	

インデックス	
      1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
        1	
   3	
   1	
   2	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
         1	
   2	
   3	
   2	
   2	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理 辻褄合わせ)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)
    元遷移先からさらに遷移されるノードを確認
    コピー元のBASE値が0より大きければ、
    bの元遷移先「3」からさらに遷移した先が存在するので確認
    (先ほどと同様に線形探索を行いCHECK[i] = 3となるノードを探す)



                                                                       文字コード表N(c)	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
      bの元遷移先	
                     #           bの新しい遷移先	
              1	
   2	
   3	
   4	
   5	
   6	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   1	
   2	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   2	
   2	
   3	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理 辻褄合わせ)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)
    元遷移先からさらに遷移される先のCHECK値を更新
    新しい遷移先5からの状態遷移となるため, CHECK[7] = 5 に更新




                                                                       文字コード表N(c)	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
      bの元遷移先	
                                 bの新しい遷移先	
              1	
   2	
   3	
   4	
   5	
   6	
                                         #

インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   1	
   2	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   2	
   3	
   2	
   2	
   5	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(衝突処理 辻褄合わせ)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える)
    元遷移先を初期化
    bの元遷移先を未使用ノードに設定




                                                                       文字コード表N(c)	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
      bの元遷移先	
                                                         1	
   2	
   3	
   4	
   5	
   6	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   2	
   1	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   3	
   2	
   2	
   5	
   4	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0
語彙の挿入(残りの文字列の挿入)	
    例) 現在のDouble Arrayに「ac#」を挿入する場合(「#」の遷移先を考える)
    残りの文字列の挿入
    残りの文字は「#」のみのため「#」を挿入すれば「ac#」の挿入処理は完了する

    現在「c」で遷移した状態番号は「6」
    BASE値を同様の手順で求めBASE[6] = 3を設定する
    遷移先tはt = BASE[6] + N(#) = 3 + 6 = 9となるので
    CHECK[9] = 6を設定する
                                                                       文字コード表N(c)	
                                                                       a	
   b	
   c	
   d	
   e	
   #	
                             cの遷移先	
                                   1	
   2	
   3	
   4	
   5	
   6	


インデックス	
   1	
   2	
   3	
   4	
   5	
   6	
   7	
   8	
   9	
 10	
 11	
 12	
 13	
 14	
 15	
 16	
BASE	
     1	
   3	
   0	
   2	
   1	
   3	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	
   0	

CHECK	
 0	
      1	
   0	
   3	
   2	
   2	
   5	
   4	
   6	
   0	
   0	
   0	
   0	
   0	
   0	
   0
とりあえず終わり?

More Related Content

What's hot

すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!Genya Murakami
 
プログラミングコンテストでのデータ構造 2 ~動的木編~
プログラミングコンテストでのデータ構造 2 ~動的木編~プログラミングコンテストでのデータ構造 2 ~動的木編~
プログラミングコンテストでのデータ構造 2 ~動的木編~Takuya Akiba
 
Rolling Hashを殺す話
Rolling Hashを殺す話Rolling Hashを殺す話
Rolling Hashを殺す話Nagisa Eto
 
Union find(素集合データ構造)
Union find(素集合データ構造)Union find(素集合データ構造)
Union find(素集合データ構造)AtCoder Inc.
 
Re永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライドRe永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライドMasaki Hara
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門Norishige Fukushima
 
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~Takuya Akiba
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解く最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解くshindannin
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門natrium11321
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14Ryo Suzuki
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換AtCoder Inc.
 
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013Ryo Sakamoto
 
Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Takuya Akiba
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015CODE BLUE
 
プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法Takuya Akiba
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpsonickun
 

What's hot (20)

すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!すごい constexpr たのしくレイトレ!
すごい constexpr たのしくレイトレ!
 
プログラミングコンテストでのデータ構造 2 ~動的木編~
プログラミングコンテストでのデータ構造 2 ~動的木編~プログラミングコンテストでのデータ構造 2 ~動的木編~
プログラミングコンテストでのデータ構造 2 ~動的木編~
 
Rolling Hashを殺す話
Rolling Hashを殺す話Rolling Hashを殺す話
Rolling Hashを殺す話
 
Union find(素集合データ構造)
Union find(素集合データ構造)Union find(素集合データ構造)
Union find(素集合データ構造)
 
Re永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライドRe永続データ構造が分からない人のためのスライド
Re永続データ構造が分からない人のためのスライド
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
 
Rolling hash
Rolling hashRolling hash
Rolling hash
 
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解く最小カットを使って「燃やす埋める問題」を解く
最小カットを使って「燃やす埋める問題」を解く
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換
 
C++ マルチスレッド 入門
C++ マルチスレッド 入門C++ マルチスレッド 入門
C++ マルチスレッド 入門
 
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
GPUが100倍速いという神話をぶち殺せたらいいな ver.2013
 
Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5
 
直交領域探索
直交領域探索直交領域探索
直交領域探索
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
 
プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法プログラミングコンテストでの動的計画法
プログラミングコンテストでの動的計画法
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
 

Similar to ダブル配列の実装方法

Sort
SortSort
Sortoupc
 
Ruby紹介3(pdf)
Ruby紹介3(pdf)Ruby紹介3(pdf)
Ruby紹介3(pdf)Gohryuh
 
X hago2 shortcoding 20110827
X hago2 shortcoding 20110827X hago2 shortcoding 20110827
X hago2 shortcoding 20110827uskey512
 
形式手法とalloyの紹介
形式手法とalloyの紹介形式手法とalloyの紹介
形式手法とalloyの紹介Daisuke Tanaka
 
Usp友の会勉強会、ジャクソン構造図の巻(後編)
Usp友の会勉強会、ジャクソン構造図の巻(後編)Usp友の会勉強会、ジャクソン構造図の巻(後編)
Usp友の会勉強会、ジャクソン構造図の巻(後編)umidori
 
2011年11月11日
2011年11月11日2011年11月11日
2011年11月11日nukaemon
 
Usp友の会勉強会、ジャクソン構造図の巻(前編)
Usp友の会勉強会、ジャクソン構造図の巻(前編)Usp友の会勉強会、ジャクソン構造図の巻(前編)
Usp友の会勉強会、ジャクソン構造図の巻(前編)umidori
 

Similar to ダブル配列の実装方法 (10)

Sort
SortSort
Sort
 
Ruby紹介3(pdf)
Ruby紹介3(pdf)Ruby紹介3(pdf)
Ruby紹介3(pdf)
 
R_note_02_ver1.0
R_note_02_ver1.0R_note_02_ver1.0
R_note_02_ver1.0
 
X hago2 shortcoding 20110827
X hago2 shortcoding 20110827X hago2 shortcoding 20110827
X hago2 shortcoding 20110827
 
形式手法とalloyの紹介
形式手法とalloyの紹介形式手法とalloyの紹介
形式手法とalloyの紹介
 
Ssaw08 0916
Ssaw08 0916Ssaw08 0916
Ssaw08 0916
 
Usp友の会勉強会、ジャクソン構造図の巻(後編)
Usp友の会勉強会、ジャクソン構造図の巻(後編)Usp友の会勉強会、ジャクソン構造図の巻(後編)
Usp友の会勉強会、ジャクソン構造図の巻(後編)
 
2011年11月11日
2011年11月11日2011年11月11日
2011年11月11日
 
Usp友の会勉強会、ジャクソン構造図の巻(前編)
Usp友の会勉強会、ジャクソン構造図の巻(前編)Usp友の会勉強会、ジャクソン構造図の巻(前編)
Usp友の会勉強会、ジャクソン構造図の巻(前編)
 
C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0
 

Recently uploaded

LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアルLoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアルCRI Japan, Inc.
 
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用KLab Inc. / Tech
 
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptxsn679259
 
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介Hyperleger Tokyo Meetup
 
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdfネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdfTakayuki Nakayama
 
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイルLoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイルCRI Japan, Inc.
 
Utilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsUtilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsWSO2
 

Recently uploaded (7)

LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアルLoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
 
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
 
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
知識ゼロの営業マンでもできた!超速で初心者を脱する、悪魔的学習ステップ3選.pptx
 
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
 
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdfネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
 
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイルLoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
 
Utilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native IntegrationsUtilizing Ballerina for Cloud Native Integrations
Utilizing Ballerina for Cloud Native Integrations
 

ダブル配列の実装方法

  • 2. ダブル配列(Double Array)とは •  トライ木の実装方法の一つ •  トライ木とは? •  大語彙で前方一致検索を高速に行うことが可能なデータ構造 •  例えば形態素解析、かな漢字変換で利用される •  どこが単語の始まりでどこが単語の終わりかわからない時に有用 ハッシュテーブルを使ってルックアップする方法の場合   単語境界がわからないので一文字づつ文字数を増やしてルックアップするしかない   最後までいったら開始点を1文字ずらして再度ルックアップ O(n^2) (入力文字数がn) わ た し の な ま え は な か の で す 。 ・・・
  • 3. ダブル配列(Double Array)とは •  トライ木の実装方法の一つ •  トライ木とは? •  大語彙で前方一致検索を高速に行うことが可能なデータ構造 •  例えば形態素解析、かな漢字変換で利用される •  どこが単語の始まりでどこが単語の終わりかわからない時に有用 正規表現を用いる方法   語彙数が少ない場合はこれでもそれほど遅くはない   現実は何十万語を使ってのルックアップ $reg = /(和|輪綿|腸|私|わたし|野|の|名前|なまえ|名|中|野|仲|那賀|那加| ・・・  ・・・   ・・・  ・・・ ・・・|巣|す|酢)/; 入力文字数をn, 語彙数をm, 語彙の平均文字数をkとするとO(nmk) トライを用いればO(nk)での計算が可能!
  • 4. トライ木の中身 •  じゃぁトライ木はどうして効率的なのか? •  結局のところトライは決定性オートマトンの一種 •  正規表現も中身はオートマトンを実装している 正規表現 (ab#|abc#|abdef#|acde#|ade#)が生成するオートマトン 失敗したらバックトラックしてsノードから再度検索(実装による) 語彙(#は終端文字) b # ab# abc# a abdef# b c # acde# a bde# a b d e f # S a c d e # b d e #
  • 5. トライ木の構築するオートマトン •  Prefixが共通するノードについてはまとめあげて、ノード数を 少なくできる •  遷移に失敗したらその時点で該当の候補がないことが確定 (再度開始ノードから探索しなくてもよい) トライ木が構築するオートマトン 語彙(#は終端文字) ab# # abc# b c # abdef# acde# a d bde# e f # S c d e # b d e #
  • 6. トライ木の実装方法 遷移リストを用いた実装方法 状態遷移表を用いた実装方法 •  各ノードで遷移先ノードのポインタを •  各状態ごとに、ラベルに対応 保持 する遷移先ノード番号を保持 struct node { a b c d 列: ラベル node *to; 1 2 × 4 × char *labels; 2 × × 3 × } 3 × × × ◎ 4 ◎ × × × 問題点 行:ノード番号 遷移先ノードを線形探索して 問題点 調べるので遷移が遅い 遷移のない部分についても情報を保持 する必要があるためメモリを大量に消 費する
  • 7. Double Arrayとは •  状態遷移表を2つの配列(BASE, CHECK)に圧縮 状態遷移表で重要なのは以下の2種類 1.  遷移があるかないかという情報(CHECKで管理) 2.  遷移先のノード番号がどこかという情報(BASEで管理) a b c d BASE配列 1 2 × 4 × 2 × × 3 × CHECK配列 3 × × × ◎ 4 ◎ × × × 状態遷移表の高速性を維持したままで、現実的なメモリ使用量での 探索が可能!
  • 8. Double Arrayを使ったノード遷移アルゴリズム 1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る 2.  入力文字を文字コード表を用いて数字(以下cとする)に変換 (実装上はunsigned char, 終端記号はヌル文字を用いることが多い) 3.  次の状態番号(以下tとする)を下記の計算式で計算 t = base[s] + c 4.  CHECK[t]が元の状態番号sと等しいか確認する 一致すれば遷移成功 文字コード表 a b c d e # 例) 現在の状態番号を6 入力文字をaとする 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 CHECK 0 0 0 0 0 0 0 0 6 2 0 0 0 0 0 0
  • 9. Double Arrayを使ったノード遷移アルゴリズム 1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る 2.  入力文字を文字コード表を用いて数字(以下cとする)に変換 (実装上はunsigned char, 終端記号はヌル文字を用いることが多い) 3.  次の状態番号(以下tとする)を下記の計算式で計算 t = base[s] + c 4.  CHECK[t]が元の状態番号sと等しいか確認する 一致すれば遷移成功 文字コード表 a b c d e # 例) 現在の状態番号を6 入力文字をaとする 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 CHECK 0 0 0 0 0 0 0 0 6 2 0 0 0 0 0 0 t = BASE[6] + 1 = 8 + 1 = 9
  • 10. Double Arrayを使ったノード遷移アルゴリズム 1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る 2.  入力文字を文字コード表を用いて数字(以下cとする)に変換 (実装上はunsigned char, 終端記号はヌル文字を用いることが多い) 3.  次の状態番号(以下tとする)を下記の計算式で計算 t = base[s] + c 4.  CHECK[t]が元の状態番号sと等しいか確認する 一致すれば遷移成功 文字コード表 a b c d e # 例) 現在の状態番号を6 入力文字をaとする 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 CHECK 0 0 0 0 0 0 0 0 6 2 0 0 0 0 0 0 t = BASE[6] + 1 = 8 + 1 = 9
  • 11. Double Arrayを使ったノード遷移アルゴリズム 1.  現在の状態番号(配列のインデックス 以下sとする)と入力文字を受け取る 2.  入力文字を文字コード表を用いて数字(以下cとする)に変換 (実装上はunsigned char, 終端記号はヌル文字を用いることが多い) 3.  次の状態番号(以下tとする)を下記の計算式で計算 t = base[s] + c 4.  CHECK[t]が元の状態番号sと等しいか確認する 一致すれば遷移成功 文字コード表 a b c d e # 例) 現在の状態番号を6 入力文字をaとする CHECK[9] =1 2 3 4 5 6 6なので インデックス 1 2 3 4 5 6 7 8 9 遷移成功! 10 11 12 13 14 15 16 BASE 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 CHECK 0 0 0 0 0 0 0 0 6 2 0 0 0 0 0 0 t = BASE[6] + 1 = 8 + 1 = 9
  • 12. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 13. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 BASE[1] + N(a) = 1 + 1 = 2 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 14. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 一致! インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 15. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 BASE[2] + N(b) = 3 + 2 = 5 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 16. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 一致! インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 17. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 BASE[5] + N(d) = 2 + 4 = 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 18. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 一致! インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 19. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 BASE[6] + N(#) = 2 + 6 = 8 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 20. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 一致! インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 21. Double Arrayを使った検索アルゴリズム 1.  現在の状態を1にセット 2.  検索文字列の先頭から1文字を取り出し前述の遷移を行う 3.  遷移に失敗した段階で検索打ち切り. 失敗を返す 4.  遷移に成功して、検索文字列がなくなった場合, BASEの値が0以下で あれば検索成功 成功を返す 文字コード表N(c) 例) 「abd#」で検索 a b c d e # 1 2 3 4 5 6 BASE[8] <= 0なので遷移なし 検索成功! インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 0 2 2 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 0 2 5 0 6 0 0 0 0 0 0 0 0
  • 22. Double Arrayの構築方法 1.  二つの配列は全て0で初期化する 2.  語彙を一つ一つ順次挿入していく(挿入アルゴリズムは後述) 3.  全ての語彙を挿入し終わった時点で構築終了 実用上、語彙は挿入前に全てsortしておくと、再配置が少なくなるため高速に 構築可能となる また、静的構築の場合、より高速に構築する手法もある。 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  • 23. 語彙の挿入 1.  まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する (検索に成功した場合は既に語彙があるため挿入の必要なし) 2.  遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する 使用済みノード(CHECK値>0)なら衝突処理を行う 3.  残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し, 以後残りの文字列について2 => 3 を繰り返す 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # 1 2 3 4 5 6 「ab#」が追加された状態のDouble Array インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 0 0 0 3 0 0 0 0 0 0 0 0 0
  • 24. 語彙の挿入 1.  まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する (検索に成功した場合は既に語彙があるため挿入の必要なし) 2.  遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する 使用済みノード(CHECK値>0)なら衝突処理を行う 3.  残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し, 以後残りの文字列について2 => 3 を繰り返す 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # cの文字で遷移しようとした時に失敗 1 2 3 4 5 6 そのときの状態番号は「3」 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 0 0 0 3 0 0 0 0 0 0 0 0 0
  • 25. 語彙の挿入 1.  まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する (検索に成功した場合は既に語彙があるため挿入の必要なし) 2.  遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する 使用済みノード(CHECK値>0)なら衝突処理を行う 3.  残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し, 以後残りの文字列について2 => 3 を繰り返す 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # 現在のBASE値から次の状態を計算 t = BASE[3] + N(c) = 1 + 3 = 4 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 0 0 0 3 0 0 0 0 0 0 0 0 0
  • 26. 語彙の挿入(CHECKの設定) 1.  まず追加したい語彙で検索を行い、失敗した時の状態番号を取得する (検索に成功した場合は既に語彙があるため挿入の必要なし) 2.  遷移先のCHECK値を確認 未使用ノード(CHECK値=0)ならCHECK値を更新する 使用済みノード(CHECK値>0)なら衝突処理を行う 3.  残りの文字列を追加するために遷移先ノードで適切なBASE値を設定し, 以後残りの文字列について2 => 3 を繰り返す 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # CHECK[4] <= 0より未使用ノードなので CHECK[4] = 3を設定 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 0 0 0 0 0 0 0 0 0
  • 27. 語彙の挿入(BASEの設定) 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # BASE値は、下記の手順で求める 1 2 3 4 5 6 (入力 現在の状態番号, 遷移文字) 1.  BASE値の初期値を1に設定 2.  現在のBASE値で遷移先ノードを計算 遷移先ノードのCHECK値を確認して、未使用ノードなら3へ 使用済みノードであればBASE値をインクリメントして再度2を実行 3.  BASE値をBASE[現在の状態番号]に設定 4.  CHECK[遷移先ノード]に現在の状態番号を設定 1 + N(#) = 7 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 0 0 0 0 0 0 0 0 0
  • 28. 語彙の挿入(BASEの設定) 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # BASE値は、下記の手順で求める 1 2 3 4 5 6 (入力 現在の状態番号, 遷移文字) 1.  BASE値の初期値を1に設定 2.  現在のBASE値で遷移先ノードを計算 遷移先ノードのCHECK値を確認して、未使用ノードなら3へ 使用済みノードであればBASE値をインクリメントして再度2を実行 3.  BASE値をBASE[現在の状態番号]に設定 4.  CHECK[遷移先ノード]に現在の状態番号を設定 CHECK[7] > 0のため 使用済み要素 BASE値=1は不適 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 0 0 0 0 0 0 0 0 0
  • 29. 語彙の挿入(BASEの設定) 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # BASE値は、下記の手順で求める 1 2 3 4 5 6 (入力 現在の状態番号, 遷移文字) 1.  BASE値の初期値を1に設定 2.  現在のBASE値で遷移先ノードを計算 遷移先ノードのCHECK値を確認して、未使用ノードなら3へ 使用済みノードであればBASE値をインクリメントして再度2を実行 3.  BASE値をBASE[現在の状態番号]に設定 4.  CHECK[遷移先ノード]に現在の状態番号を設定 2 + N(#) = 8 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 0 0 0 0 0 0 0 0 0
  • 30. 語彙の挿入(BASEの設定) 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # BASE値は、下記の手順で求める 1 2 3 4 5 6 (入力 現在の状態番号, 遷移文字) 1.  BASE値の初期値を1に設定 2.  現在のBASE値で遷移先ノードを計算 遷移先ノードのCHECK値を確認して、未使用ノードなら3へ 使用済みノードであればBASE値をインクリメントして再度2を実行 3.  BASE値をBASE[現在の状態番号]に設定 4.  CHECK[遷移先ノード]に現在の状態番号を設定 CHECK[8] <= 0のため 未使用ノード BASE値=2はOK インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 0 0 0 0 0 0 0 0 0
  • 31. 語彙の挿入(BASEの設定) 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # BASE値は、下記の手順で求める 1 2 3 4 5 6 (入力 現在の状態番号, 遷移文字) 1.  BASE値の初期値を1に設定 2.  現在のBASE値で遷移先ノードを計算 遷移先ノードのCHECK値を確認して、未使用ノードなら3へ 使用済みノードであればBASE値をインクリメントして再度2を実行 3.  BASE値をBASE[現在の状態番号]に設定 4.  CHECK[遷移先ノード]に現在の状態番号を設定 BASE[4] = 2を設定 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 0 0 0 0 0 0 0 0 0
  • 32. 語彙の挿入(BASEの設定) 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # BASE値は、下記の手順で求める 1 2 3 4 5 6 (入力 現在の状態番号, 遷移文字) 1.  BASE値の初期値を1に設定 2.  現在のBASE値で遷移先ノードを計算 遷移先ノードのCHECK値を確認して、未使用ノードなら3へ 使用済みノードであればBASE値をインクリメントして再度2を実行 3.  BASE値をBASE[現在の状態番号]に設定 4.  CHECK[遷移先ノード]に現在の状態番号を設定 CHECK[BASE[4]+6] = CHECK[8] = 4 を設定 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 33. 語彙の挿入(完了) 例) 既に「ab#」という語彙が追加されている時に、 文字コード表N(c) 新たに「abc#」という語彙を追加するとする a b c d e # 1 2 3 4 5 6 「abc#」全ての遷移のBASE, CHECK値の挿入が 完了したので挿入操作終了 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 34. 語彙の挿入(衝突の場合) •  次は挿入時に衝突がある場合を考える 例) 現在のDouble Arrayに「ac#」を挿入する場合 基本的な手順は先述の場合と同じ 文字コード表N(c) a b c d e # cの文字で遷移しようとした時に失敗 1 2 3 4 5 6 そのときの状態番号は「2」 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 35. 語彙の挿入(衝突の場合) 例) 現在のDouble Arrayに「ac#」を挿入する場合 基本的な手順は先述の場合と同じ 文字コード表N(c) 現在のBASE値から遷移先を計算 a b c d e # t = BASE[2] + N(c) = 1 + 3 = 4 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 36. 語彙の挿入(衝突の場合) 例) 現在のDouble Arrayに「ac#」を挿入する場合 基本的な手順は先述の場合と同じ 文字コード表N(c) a b c d e # CHECK[4] > 0のため 1 2 3 4 5 6 使用済みノード BASE[2]の再設定が必要となる インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 37. 語彙の挿入(衝突処理) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) 衝突処理 1.  現在のノード番号から遷移している全てのラベル(文字)を取得 2.  全てのラベルが遷移可能なBASE値及び上記ラベルの新しい遷移先を求める 3.  上記ラベルについて、それぞれ古い遷移先のBASE値を新しい遷移先にコピー 4.  古い遷移先のさらに遷移される先のCHECK値も新しい遷移先に更新 5.  各ラベルの古い遷移先のノードを未使用状態に初期化 文字コード表N(c) a b c d e # 1 2 3 4 5 6 現在注目中のノード インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 38. 語彙の挿入(衝突処理) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) 遷移ラベル(文字)の取得 これは「CHECK[i] = 現在のノード番号」となる ノードを線形探索すればいい 下記の図の場合「3」が該当する 文字コード表N(c) a b c d e # 1 2 3 4 5 6 b インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 39. 語彙の挿入(衝突処理) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) BASE値の決定 新しいBASE[2]の値として「b」「c」の2文字が遷移可能な BASE値を探す 手順については先述と同様 複数の遷移ラベルで確認する必要があることに注意 結果、新しいBASE値3が求まる 今までのBASE[2] = 1を一時変数に保存し、 BASE[2] = 3を設定する 文字コード表N(c) b a b c d e # 1 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 0 0 3 4 0 0 0 0 0 0 0 0
  • 40. 語彙の挿入(衝突処理) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) 遷移先CHECK値の更新 「b」「c」の新しい遷移先でCHECK値を設定する 「b」の新しい遷移先は5 「c」の遷移先は6 文字コード表N(c) b a b c d e # 1 1 2 3 4 5 6 b c インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 1 2 0 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 2 2 3 4 0 0 0 0 0 0 0 0
  • 41. 語彙の挿入(衝突処理 辻褄合わせ) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) 元遷移先のBASE値コピー 「b」の元遷移先「3」のBASE値を, 新しい遷移先「5」のBASE値ににコピー 文字コード表N(c) a b c d e # b 1 2 3 4 5 6 1 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 1 2 1 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 2 2 3 4 0 0 0 0 0 0 0 0
  • 42. 語彙の挿入(衝突処理 辻褄合わせ) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) 元遷移先からさらに遷移されるノードを確認 コピー元のBASE値が0より大きければ、 bの元遷移先「3」からさらに遷移した先が存在するので確認 (先ほどと同様に線形探索を行いCHECK[i] = 3となるノードを探す) 文字コード表N(c) a b c d e # bの元遷移先 # bの新しい遷移先 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 1 2 1 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 2 2 3 4 0 0 0 0 0 0 0 0
  • 43. 語彙の挿入(衝突処理 辻褄合わせ) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) 元遷移先からさらに遷移される先のCHECK値を更新 新しい遷移先5からの状態遷移となるため, CHECK[7] = 5 に更新 文字コード表N(c) a b c d e # bの元遷移先 bの新しい遷移先 1 2 3 4 5 6 # インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 1 2 1 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 2 3 2 2 5 4 0 0 0 0 0 0 0 0
  • 44. 語彙の挿入(衝突処理 辻褄合わせ) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「c」の遷移先を考える) 元遷移先を初期化 bの元遷移先を未使用ノードに設定 文字コード表N(c) a b c d e # bの元遷移先 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 2 1 0 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 3 2 2 5 4 0 0 0 0 0 0 0 0
  • 45. 語彙の挿入(残りの文字列の挿入) 例) 現在のDouble Arrayに「ac#」を挿入する場合(「#」の遷移先を考える) 残りの文字列の挿入 残りの文字は「#」のみのため「#」を挿入すれば「ac#」の挿入処理は完了する 現在「c」で遷移した状態番号は「6」 BASE値を同様の手順で求めBASE[6] = 3を設定する 遷移先tはt = BASE[6] + N(#) = 3 + 6 = 9となるので CHECK[9] = 6を設定する 文字コード表N(c) a b c d e # cの遷移先 1 2 3 4 5 6 インデックス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BASE 1 3 0 2 1 3 0 0 0 0 0 0 0 0 0 0 CHECK 0 1 0 3 2 2 5 4 6 0 0 0 0 0 0 0