Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
全文検索のアルゴリズムとデータ構造
Search
toyama
December 13, 2020
0
240
全文検索のアルゴリズムとデータ構造
toyama
December 13, 2020
Tweet
Share
More Decks by toyama
See All by toyama
Path Copying による永続データ構造
toyama1710
0
520
Featured
See All Featured
Code Reviewing Like a Champion
maltzj
521
39k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
27
1.5k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.8k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Rails Girls Zürich Keynote
gr2m
94
13k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.8k
The Power of CSS Pseudo Elements
geoffreycrofte
75
5.4k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
44
9.4k
The Invisible Side of Design
smashingmag
299
50k
Building Your Own Lightsaber
phodgson
104
6.2k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
Gamification - CAS2011
davidbonilla
80
5.1k
Transcript
Hugo でブログ作成 会津大学 学部1年 toyama1710
自己紹介 • HN: 遠山 • Twitter: @toyama_pts • github: toyama1710
• 趣味: 絵, バイク
はじめに • ブログを作りました ◦ https://blog.toyama1710.net • 見てね!
Hugo • SSG の一種 • markdown で書いた記事を html に変換してくれる
記事検索機能 • タグ検索 • カテゴリ検索
記事検索機能 • タグ検索 • カテゴリ検索 全文検索も欲しくないスカ?
文字列検索アルゴリズム Hugo でブログ作成 会津大学 学部1年 toyama1710 勉強したので知見共有するぜ!
問題設定
パターンマッチング 文字列 T は部分文字列として文字列 P を含むか? を判定する問題
パターンマッチング 例 • “アメリカンジョーク” に “カンジョー” は含まれる?
パターンマッチング 例 • “アメリカンジョーク” に “カンジョー” は含まれる? ◦ Yes
パターンマッチング 例 • “アメリカンジョーク” に “カンジョー” は含まれる? ◦ Yes •
“任意コードジッコウ” に “ドジッコ” は含まれる?
パターンマッチング 例 • “アメリカンジョーク” に “カンジョー” は含まれる? ◦ Yes •
“任意コードジッコウ” に “ドジッコ” は含まれる? ◦ Yes
パターンマッチング 例 • “アメリカンジョーク” に “カンジョー” は含まれる? ◦ Yes •
“任意コードジッコウ” に “ドジッコ” は含まれる? ◦ Yes • “Linux” に “Linx” は含まれる?
パターンマッチング 例 • “アメリカンジョーク” に “カンジョー” は含まれる? ◦ Yes •
“任意コードジッコウ” に “ドジッコ” は含まれる? ◦ Yes • “Linux” に “Linx” は含まれる? ◦ No
記法,用語の導入 • 文字列 T の長さを |T| と書く ◦ T =
“abc” のとき |T| = 3 ◦ S = “efghi” のとき |S| = 5
記法,用語の導入 • prefix ◦ 文字列の先頭部分を抜き出してきた文字列 ◦ “ABC” の prefix -
“ABC”, “AB”, “A”, “” • suffix ◦ 文字列の末尾部分を抜き出した文字列 ◦ “ABC” の suffix - “ABC”, “BC”, “C”, “”
注意 • 今回の LT では定数アルファベットを仮定 ◦ 文字の種類数は定数
総当たり
総当たり • T と P を一字ずつずらして一致比較 • 途中で異なる文字があれば T をずらしてもう一度
A I Z U U N I V U N I V P: T:
総当たり • T と P を一字ずつずらして一致比較 • 途中で異なる文字があれば T をずらしてもう一度
A I Z U U N I V U N I V P: T:
総当たり • T と P を一字ずつずらして一致比較 • 途中で異なる文字があれば T をずらしてもう一度
A I Z U U N I V U N I V P: T:
総当たり • T と P を一字ずつずらして一致比較 • 途中で異なる文字があれば T をずらしてもう一度
A I Z U U N I V U N I V P: T:
総当たり • T と P を一字ずつずらして一致比較 • 途中で異なる文字があれば T をずらしてもう一度
A I Z U U N I V U N I V P: T:
総当たり • T と P を一字ずつずらして一致比較 • 途中で異なる文字があれば T をずらしてもう一度
• 時間計算量: O(|T||P|) A I Z U U N I V U N I V P: T:
総当たり 1. 狂った僕が あああああああああああ....(100万字) とだけ書かれた記事を投稿
総当たり 1. 狂った僕が あああああああああああ....(100万字) とだけ書かれた記事を投稿 2. ユーザも狂って検索窓に ああああああああ....(100万+1字)
総当たり 1. 狂った僕が あああああああああああ....(100万字) とだけ書かれた記事を投稿 2. ユーザも狂って検索窓に ああああああああ....(100万+1字) 3. 検索が終わらない
ラビンカープ法
ラビンカープ法 • 基本方針 ◦ 文字列の比較をハッシュで行う ◦ O(|P|) -> O(1) へ改善!!
◦ 全体として O(|T| + |P|)
Rolling Hash
Rolling Hash - ラビンカープ法 • 部分文字列のハッシュを高速に計算できる • 文字列を B 進数として解釈して
MOD をとるだけ ◦ B > 1 の正整数 ◦ 素数で MOD を取ると一様分布しやすい
Rolling Hash - ラビンカープ法 • 文字列 S のハッシュ値が既に計算済み ◦ S
の末尾に 1 文字付け加える: O(1) ◦ S の先頭を 1 文字削除: O(1) • 検索位置をスライドしながらハッシュ値を計算可能 A I Z U U N I V U N I V P: T:
Rolling Hash - ラビンカープ法 • 文字列 S のハッシュ値 h ◦
S.push(c): h * B + c ◦ S.erase(0): h - S[0] * B^|P| A I Z U U N I V U N I V P: T:
Rolling Hash - ラビンカープ法 • 文字列 S のハッシュ値 h ◦
S.push(c): h * B + c ◦ S.erase(0): h - S[0] * B^|P| A I Z U U N I V U N I V P: T:
Rolling Hash - ラビンカープ法 • 文字列 S のハッシュ値 h ◦
S.push(c): h * B + c ◦ S.erase(0): h - S[0] * B^|P| • 検索位置のスライドが O(1) A I Z U U N I V U N I V P: T:
Rolling Hash - ラビンカープ法 • 文字列 S のハッシュ値 h ◦
S.push(c): h * B + c ◦ S.erase(0): h - S[0] * B^|P| • 検索位置のスライドが O(1) • O(|T| + |P|) A I Z U U N I V U N I V P: T:
Rolling Hash - ラビンカープ法 • 記事全体の文字数は 1万字を超える • ユーザが検索する語は 30
字も無いはず • まだ改善できそう
Suffix Trie お気に入りデータ構造
Suffix Trie の前に Trie について お気に入りデータ構造
Trie • こんな感じの木構造 • 根から頂点へのパスが単語を表す • apple • apart •
bug • bag a p p l e a r t b u g a g
Trie • 共通した prefix を持った単語の列挙が得意 • 根からラベルを辿っていくだけで OK • prefix:
ap • prefix: bu a p p l e a r t b u g a g
Suffix Trie 再び
Suffix Trie • Thm. ◦ 部分文字列は suffix の prefix
Suffix Trie • Thm. ◦ 部分文字列は suffix の prefix A
I Z U U N I V T: U U N I V U U
Suffix Trie • T の suffix を全部 Trie に入れちゃお!w •
マジでこれだけ T = “ababc” a b a b c c b a b c c c
Suffix Trie • T の suffix を全部 Trie に入れちゃお!w •
マジでこれだけ • 構築時間: O(|T|^2) • 空間計算量: O(|T|^2) • 検索時間: クエリ毎 O(|P|)
Suffix Trie • 流石にメモリを食いすぎ • 1万字で 1 GB 食う計算
DAWG - Directed Acyclic Word Graph Suffix Automaton の名でも親しまれる
DAWG • Suffix Trie をよく見ると全く同じ部分木がたくさん • 一つにまとめてしまおう T = “ababc”
a b a b c c b a b c c c
DAWG • 検索は Suffix Trie と同様始点から辺を辿っていく a b a b
c c b a b c c c
DAWG • 構築時間: O(|T|) • 空間計算量: O(|T|) • 検索時間: クエリ毎
O(|P|) • 満足しました
実測 - DAWG
実測 • AOJ ALDS1 - 14 D を解いてみます ◦ https://onlinejudge.u-aizu.ac.jp/courses/lesson/1/ALDS1/14/
ALDS1_14_D • 冒頭の問題設定と全く同じ
実測 • AOJ ALDS1 - 14 D を解いてみます ◦ https://onlinejudge.u-aizu.ac.jp/courses/lesson/1/ALDS1/14/
ALDS1_14_D • 冒頭の問題設定と全く同じ • |T| < 1,000,000 • |P| < 1,000 • クエリ数 10,000
実測 • はやい • はやいと、うれしい!!
参考文献 • 秋葉拓哉, 岩田陽一, 北川宣稔 プログラミングコンテストチャレンジ ブック[第二版] - マイナビ出版 •
Anselm Blumer, Janet Blumer, David Haussler, Andrzej Ehrenfeucht, M. T. Chen, and Joel I. Seiferas. The smallest automation recognizing the subwords of a text. Theoretical Computer Science, 40:31–55, 1985.
ありがとうございました 楽しんでいただけたなら幸いです