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
320
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
全文検索のアルゴリズムとデータ構造
toyama
December 13, 2020
More Decks by toyama
See All by toyama
Path Copying による永続データ構造
toyama1710
0
700
Featured
See All Featured
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
300
Into the Great Unknown - MozCon
thekraken
41
2.6k
The Limits of Empathy - UXLibs8
cassininazir
1
350
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
250
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
200
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
200
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
460
Optimising Largest Contentful Paint
csswizardry
37
3.7k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
380
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
610
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
A Tale of Four Properties
chriscoyier
163
24k
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.
ありがとうございました 楽しんでいただけたなら幸いです