FM-indexを用いた複数テキストの全文検索
FM-index は、文字列の全文検索を効率的に行うためのデータ構造(索引)です。あらかじめテキストのFM-indexを構築しておくことで、指定された検索パターンに関するクエリを高速に処理することができます。
クエリ | 説明 |
---|---|
テキスト中のパターンの出現回数を数える | |
テキスト中のパターンの出現位置を計算する |
特に、
FM-index は簡潔データ構造や Burrows-Wheeler 変換といった要素で構成されており、索引を圧縮した形で扱うことができます(圧縮全文索引)。また、索引自体が元のテキストの情報を保持しているので(自己索引)、検索結果のパターンに隣接するテキストを復元することもできます。
FM-index は単一テキストにおける検索機能を提供しますが、その自然な応用として複数テキストの集合に対する検索が挙げられます。 例えば、複数のファイルの中から特定のキーワードを含むファイルを列挙したい、といったことが往々にしてあります。本記事では、FM-indexを用いてこのような複数テキストの検索を実現する方法を紹介します。
FM-index
複数テキストにおける検索の話に入る前に、まずは単一テキストに対するFM-indexについておさらいします。
テキスト
FM-index の構築
FM-index は以下の要素で構成されます。
-
:テキストT_\mathrm{BWT} をBurrows-Wheeler変換した文字列。テキストの接尾辞配列から構築できます。T -
:各アルファベットC について、テキストc に含まれるT より小さい文字の出現回数を格納した配列。c -
:テキストSA の接尾辞配列(suffix array)。T クエリの回答に必要です。サイズ削減のため、完全な接尾辞配列ではなく、等間隔にサンプルしたものを用いることができます。\mathrm{locate}
SA )
接尾辞配列(テキスト
接尾辞(辞書順) | 開始位置 |
---|---|
6 | |
5 | |
3 | |
1 | |
0 | |
4 | |
2 |
接尾辞配列は、SA-IS[1]などのアルゴリズムを用いることで
T_\mathrm{BWT} )
Burrows-Wheeler 変換(Burrows-Wheeler 変換は、テキストのすべての回転を辞書順に並べ、各回転の末尾の文字を順に抽出した文字列を得る変換です。例えば、
k SA[k] F L
-----------------
0 6 $banana
1 5 a$banan
2 3 ana$ban
3 1 anana$b
4 0 banana$
5 4 na$bana
6 2 nana$ba
テキスト
ここで、回転の順序は接尾辞配列の順序と同一です。表の行
表の先頭の列をF列、末尾の列をL列と呼びます。例では
Burrows-Wheeler変換されたテキスト
-
:\mathrm{rank}_c(T_\mathrm{BWT}, i) 中の文字T_\mathrm{BWT}[0, i) の出現回数を数えるc -
:\mathrm{select}_c(T_\mathrm{BWT}, i) の中のT_\mathrm{BWT} 番目のi + 1 の出現位置を計算するc -
:\mathrm{access}(T_\mathrm{BWT}, i) を求めるT_\mathrm{BWT}[i]
データのサイズや構築・クエリ効率は使用するウェーブレット木の実装によりますが、ウェーブレット行列を用いた場合の計算量は以下のようになります。
- サイズ:
O(n \log\sigma) - 構築時間:
O(n \log\sigma) -
,\mathrm{rank} の計算時間:\mathrm{select} O(\log\sigma)
C )
文字の出現回数(配列
実用上、
FM-index のクエリ
例として、テキスト
k SA[k] F L
-----------------------
0 11 $mississippi
1 10 i$mississipp
2 7 ippi$mississ
3 4 issippi$miss
4 1 ississippi$m
5 0 mississippi$
6 9 pi$mississip
7 8 ppi$mississi
8 6 sippi$missis
9 3 sissippi$mis
10 2 ssippi$missi
11 5 ssissippi$mi
FM-indexは、Burrows-Wheeler変換された文字列
重要な性質として、テキストに出現するパターンに対応する接尾辞、すなわちパターンで開始する接尾辞は、この表の中の連続する行に現れます。例えば、パターン
パターン
例として、
F列において、文字
k SA[k] F L
-----------------------
0 11 $mississippi
C[i] 1 10 i$mississipp
2 7 ippi$mississ
s 3 4 *issippi$miss <-+
| 4 1 *ississippi$m <-|--+
e 5 0 mississippi$ | |
6 9 pi$mississip | |
7 8 ppi$mississi | |
s' 8 6 sippi$missis | |
| 9 3 sissippi$mis | |
| 10 2 ssippi$missi*--+ |
| 11 5 ssissippi$mi*-----+
e'
したがって、範囲の初期値を
パターンの出現回数(count)
パターンの出現回数(
パターンの出現位置(locate)
パターンの出現位置(
ここで、
この復元に用いられるのがLF-mappingです。LF-mapping(
また、LF-mapping は
ここで、
すなわち、
複数テキストのための FM-index
FM-indexを用いて、
- 検索パターンを含むテキスト片の識別子の一覧を取得する
- 検索パターンの各テキスト片内での出現位置を取得する
- 検索パターンを接頭辞または接尾辞として含むテキスト片の識別子の一覧を取得する
複数のテキストに対する全文検索を実現するためには、いくつかのアプローチが考えられます。これらのアプローチは、テキストをどのように結合して単一の大きなテキスト
テキストの結合方法 | ||
---|---|---|
1 | 異なる終端文字で結合 | |
2 | 同一の終端文字で結合 | |
3 | 終端文字なしで結合 |
異なる終端文字で結合
この方法は、XML文書上の高速なXPathクエリ処理を実現するSXSI[2]で採用されています。各テキスト片
Since there are several $’s in
, we fix a special ordering such that the end-marker of the T -th text appears at i in M F[i]
構築
検索した位置をテキスト片の識別子と紐づけるために、
例として、テキスト
k SA[k] F L
-----------------------
0 3 $bar$baz$foo
1 7 $baz$foo$bar
2 11 $foo$bar$baz
3 5 ar$baz$foo$b
4 9 az$foo$bar$b
5 4 bar$baz$foo$ L[5] = $_0
6 8 baz$foo$bar$ L[6] = $_1
7 0 foo$bar$baz$ L[7] = $_2
8 1 oo$bar$baz$f
9 2 o$bar$baz$fo
10 6 r$baz$foo$ba
11 10 z$foo$bar$ba
テキストの
5 | 0 | 1 | |
6 | 1 | 2 | |
7 | 2 | 0 |
またクエリの効率化のために、サンプリングした
クエリ
与えられたパターン
-
またはL[k] = \text\textdollar がk でサンプルされた位置の場合、停止する。\mathrm{Pos} -
とする。k \gets LF(k)
計算を停止した位置
-
の場合、L[k] = \text\textdollar x = \mathrm{Doc}[\mathrm{rank}_\text\textdollar(L,k)] -
がk でサンプルされた位置である場合、\mathrm{Pos} を参照してテキストの識別子\mathrm{Pos} を取得するx
また、繰り返しの回数および
パターン
パターン
計算量
この方法では、単一テキストのためのFM-indexと比較して、追加で以下のデータが必要になります。
- テキスト
に保存する各テキスト片の終端文字T \mathrm{Doc} \mathrm{Pos}
追加される終端文字の数は
パターンのある出現位置について、対応するテキスト片の識別子および相対位置を計算する際の時間は
同一の終端文字で結合
この方法では、各テキスト片
テキスト
k SA[k] F L
-----------------------
0 11 $foo$bar$baz
1 3 $bar$baz$foo
2 7 $baz$foo$bar
3 5 ar$baz$foo$b
4 9 az$foo$bar$b
5 4 bar$baz$foo$
6 8 baz$foo$bar$
7 0 foo$bar$baz$
8 2 o$bar$baz$fo
9 1 oo$bar$baz$f
10 6 r$baz$foo$ba
11 10 z$foo$bar$ba
この方式で構築したFM-indexに対し、そのまま通常のLF-mapping
例えば、
となります。F列の
LF-mappingは
一方で、終端文字
そこで、終端文字に順序を導入する代わりに、テキスト全体
構築
クエリ
k SA[k] F L
-----------------------
0 11 $ z F[0] = $_2
1 3 $bar$baz$ o F[1] = $_0
2 7 $baz$ r F[2] = $_1
3 5 ar$baz$ b
4 9 az$ b
5 4 bar$baz$ $ L[5] = $_0
6 8 baz$ $ L[6] = $_1
7 0 foo$bar$baz$ L[7] = $_2
8 2 o$bar$baz$ o
9 1 oo$bar$baz$f
10 6 r$baz$ a
11 10 z$ a
この例では
L列中で文字が
-
の場合、LF-mappingはk = k_0 に対して必ずL[k_0] = \text\textdollar_{d-1} を対応づけます。これはF[0] が最小の接尾辞であるためです。例では、\text\textdollar_{d-1} がL[7] = \text\textdollar_2 に対応します。F[0] - それ以外の終端文字については、その出現順がL列とF列で一致します。これは、
の行に対応する接尾辞L[k] = \text\textdollar_x に対し、T[SA[k], n-1] の行に対応する接尾辞がLF(k) となり、接尾辞の順序が保存されるためです。例では、T[SA[LF(k)], n-1] = \text\textdollar_x T[SA[k], n-1] 、L[5]=\text\textdollar_0 がF列においてL[6]=\text\textdollar_1 、F[1] に同じ順序で出現しています。F[2]
よって、
終端文字なしで結合
この方法では、終端文字を用いずにすべてのテキスト片を単純に結合したテキスト
このために、結合したテキスト
- 結合されたテキストの文字
があるテキスト片の最後の文字である場合、T[i] とする。B[i] = 1 - それ以外の場合、
とする。B[i] = 0
ビット列
構築
ビット列
クエリ
また、パターン
- テキスト片
内に出現している場合(T_0 )は、結合テキスト全体における出現位置x = 0 がそのまま相対位置となります。i - テキスト片
内に出現している場合(T_x )は、テキスト片x > 0 の開始位置がT_x であることから、相対位置は\mathrm{select_1}(B, x - 1) + 1 です。i - (\mathrm{select_1}(B, x - 1) + 1)
例えば、テキスト
x 0 1 2
i 0123456789
--------------
T = foobarbaz$
B = 0010010010
--------------
012
パターン
また、
計算量
単一テキストのFM-indexに加えて、ビット列
まとめ
複数のテキストに対する高速な文字列検索が、FM-indexを用いてどのように実現できるかを紹介しました。
紹介した内容は出典した文献を元にしていますが、筆者がFM-indexを実装中に考えたアイディアも一部あります。筆者が発見できなかった、より標準的で効率的な手法があるかもしれません[4]。
-
Ge Nong, Sen Zhang, & Wai Hong Chan. (2011). Two Efficient Algorithms for Linear Time Suffix Array Construction. IEEE Transactions on Computers, 60(10), 1471–1484. https://doi.org/10.1109/TC.2010.188 ↩︎
-
Arroyuelo, A., Claude, F., Maneth, S., Mäkinen, V., Navarro, G., Nguyen, K., Siren, J., & Välimäki, N. (2011). Fast In-Memory XPath Search over Compressed Text and Tree Indexes (No. arXiv:0907.2089). arXiv. https://doi.org/10.48550/arXiv.0907.2089 ↩︎
-
Navarro, G., & Providel, E. (2012). Fast, small, simple rank/select on bitmaps. In International Symposium on Experimental Algorithms (pp. 295-306). Berlin, Heidelberg: Springer Berlin Heidelberg. https://doi.org/10.1007/978-3-642-30850-5_26 ↩︎
-
特に、Compact Data Structures は分野の大家であるNavarro先生による書籍で、簡潔データ構造や索引に関する幅広いトピックをカバーしているようです。複数テキストへの応用についても言及されているかもしれません(未読)。 ↩︎
Discussion