520 likes | 646 Views
情報システム基盤学基礎 1. Elements of Information Systems Fundamentals1. アルゴリズムとデータ構造 第 6 回. 目次 : 今日やること. 文字列のアルゴリズム. 文字列 照合 Knuth-Morris-Pratt の アルゴリズム Boyer-Moore のアルゴリズム Rabin-Karp のアルゴリズム. 文字列照合とは ?. Input: テキスト T n 文字の配列 パターン P m 文字 の配列. Output: 文字列中でパターンが現れる 位置 pos (一番小さい位置).
E N D
情報システム基盤学基礎1 Elements of Information Systems Fundamentals1 アルゴリズムとデータ構造 第6回
目次: 今日やること 文字列のアルゴリズム • 文字列照合 • Knuth-Morris-Prattのアルゴリズム • Boyer-Mooreのアルゴリズム • Rabin-Karpのアルゴリズム
文字列照合とは? Input:テキスト Tn文字の配列 パターン Pm文字の配列 Output:文字列中でパターンが現れる位置 pos (一番小さい位置) P[1]=T[pos] P[2]=T[pos+1] … P[m]=T[pos+m-1] 1 2 3 4 5 6 7 8 9 テキスト T : 文字列照合アルゴリズム b c a b a b a c d 3 パターン P : 応用: Webページ上でのキーワード検索 a b a 遺伝子データ上でのキーワード検索
Naiveアルゴリズム アイデア: T中の各文字について, パターン Pが出現していないかどうか調べる Pos. s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c パターン P : 計算時間: a a a l l l g g g o o o l l l O(m(n-m+1))
Naiveアルゴリズムの欠点 • マッチング時に得られた情報を活用してない ⇒マッチング位置の後退が発生 aと一致しない • 2文字目は「l」 • 3文字目は「g」 aと一致しない s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c × 無駄 無駄 パターン P : a a a l l l g g g o o o l l l いきなり4文字目までずらすべき
Knuth-Morris-Prattのアルゴリズム(KMPアルゴリズム)Knuth-Morris-Prattのアルゴリズム(KMPアルゴリズム) • 過去のマッチング時の情報を利用して文字比較回数削減 • マッチング位置を後退させない • Pの何文字目でマッチングが失敗したかで以下を決定 • Tの現在のマッチング位置をPの何文字目と合わせてマッチング作業を再開するか • Tのマッチング位置は不変⇒後退しない • Pは必ず右にずれる • 配列nextはTによらず事前計算可能⇒表に記憶 何文字目で間違ったか () マッチング開始位置
マッチング失敗時の動作 s • k文字目で失敗⇒next[k]を参照 • Pを右にずらす: 分 • Tの現在のマッチング位置をPのnext[4]文字目と重ねる • 現在のマッチング位置から照合再開 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c × パターン P : next[4]=1 a a l l g g o o l l
マッチング失敗時の動作 s • k文字目で失敗⇒next[k]を参照 • Pを右にずらす: • Tの現在のマッチング位置がPのnext[k]文字目と重ねる • 現在のマッチング位置から照合再開 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l a l b g o l a h c × パターン P : next[4]=2の例 a a l l g o o l l a
Tによらず事前計算できる理由 • 文字目でマッチングが間違ったという条件から Tの直前の文字がわかる • P自身に含まれる パターン P : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a a l g o l h c a a a a a a a a a l l l l l l l l l g g g g g g g g g o o o o o o o o o l l d l l l l l l
nextの計算 • next[1]:0に設定。特殊扱い • 1文字も一致しないで間違った • 文字列Tの現在のマッチング位置が変化 • next[k]: • : (T内)一致箇所の最後(suffix) • : Pの先頭(prefix) k=3 k=5 a a a a a a a l l l g g l l l l :Tのマッチング位置 パターン P :
KMPアルゴリズムの疑似コード 計算量(文字比較回数)=2n
動作例 P : k=4 1 1 2 2 3 3 4 4 5 5 ○ × ○ ○ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 T : a l a d a l a l g h c P: k=2 ○ × 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 T : a h a h l l a a l a l a a a l l g l g l a l a d a l a l g h c
動作例 k=1 P : 1 1 2 2 3 3 4 4 5 5 × 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 T : a l a d a l a l g h c k=1 P: × 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 T : h a a h l l a a l l a a l a l a g l l g a l a d a l a l g h c
動作例 P : 1 2 3 4 5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 T : a l a d a l a l g h c a h l a a l l a l g
k=5 P: ○ ○ ○ ○ × 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 T : 1 1 2 2 3 3 4 4 5 5 a l a d a l a l g h c P: ○ ○ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 a l a d a l a l g h c T : h a a h a l a l l a l a a l a l l g l g
k=5 P: ○ ○ ○ ○ × 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 T : 1 1 2 2 3 3 4 4 5 5 a l a d a l a l g h c P: ○ ○ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 a l a d a l a l g h c T : h a a h a l a l l a l a a l a l l g l g
1 2 3 4 5 P: ○ ○ ○ ○ ○ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 a l a d a l a l g h c T : a h a l l a a l l g
KMPアルゴリズムのまとめ マッチング失敗時 • Tの現在のマッチング位置をPの何文字目と合わせてマッチング作業を再開するかを決定 • Pを右にずらすことに相当 • Pのみを用いてnext[k]を事前計算 • Tのマッチング位置は絶対に後退しない • Tのすべての文字は必ず1度は比較される ⇒改良余地
Boyer-Mooreのアルゴリズム • KMP: Pの1文字目から後ろへ比較をすすめる • Boyer-Moore: Pの最後の文字から前に比較を進める パターン P : パターン P : 失敗時にPをずらす方向はTの後方 (KMPと一緒) a a a a l l l l g g g g o o o o l l l l
Boyer-Mooreのアルゴリズム アイデア1: T中の各文字について, パターン Pが 出現していないかどうかを後ろから調べる アイデア2: 文字比較をさぼれるところはスキップする 基本的な流れ s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c a a a a l l l l g g g g o o o o l l l l パターン P :
Boyer-Mooreのアルゴリズム アイデア1: T中の各文字について, パターン Pが 出現していないかどうかを後ろから調べる アイデア2: 文字比較をさぼれるところはスキップする 基本的な流れ s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c a a a a l l l l g g g g o o o o l l l l パターン P :
Boyer-Mooreのアルゴリズム アイデア1: T中の各文字について, パターン Pが 出現していないかどうかを後ろから調べる アイデア2: 文字比較をさぼれるところはスキップする 基本的な流れ s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c a a a a l l l l g g g g o o o o l l l l パターン P :
Boyer-Mooreのアルゴリズム アイデア1: T中の各文字について, パターン Pが 出現していないかどうかを後ろから調べる アイデア2: 文字比較をさぼれるところはスキップする 基本的な流れ s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c a a a a l l l l g g g g o o o o l l l l パターン P :
Boyer-Mooreのアルゴリズム Ans. アイデア1: T中の各文字について, パターン Pが 出現していないかどうかを後ろから調べる アイデア2: 文字比較をさぼれるところはスキップする 基本的な流れ s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c a a a a l l l l g g g g o o o o l l l l パターン P :
なぜ後ろから調べるか 1文字目で間違えた時のスキップ幅を大きくできる • KMP: Pの先頭から後ろへ • Pにない文字が出現⇒1文字しかずらせない • Boyer-Moore: Pの最後から前へ • Pにない文字が出現⇒Pを大きくずらせる パターン P : 1 2 3 4 5 6 7 8 9 10 文字列 T : h l g a b パターン P : T[1..4]は比較されない n h n h e e l l v g v g o e e o l r l r 1 2 3 4 5 6 7 8 9 10 文字列 T : h l g a b
パターン中に現れない文字で間違えたら? ⇒大きくずらせる s 1 2 3 4 5 6 7 8 9 10 h l g a b 文字列 T : 文字列 T : s = 1 s = 2 s = 3 “h”のせいでマッチ しないことが明らか s = 4 Skip n n a h n n n n e e e e l l e e v v g v v v g v e e e e e o o e r r r l r l r r s = 5 マッチの 可能性あり s =6
文字比較のサボり方 2つのやり方がある Case 1:Tのマッチング位置の文字情報からスキップ幅決定 • Case 2: 2文字目以降でミスした時 • 過去に一致した文字の情報からスキップ幅決定 Case 1: Case 2: 文字列 T : 文字列 T : 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 l b a g l b a g a a l l g g o o o o l l g g o o パターン P : パターン P :
Case 1: Tのマッチング位置の文字 情報からスキップ幅決定 パターン中に現れない文字で間違えたら? ⇒ m文字ずらす 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g n e v e r s = 5 s = 6 s = 7 “z”のせいでマッチ しないことが明らか s = 8 Skip z n n a n n a n n e e e e e l l e v v g v v g v v e o e e o e e e r r r r l r r s = 9 マッチの 可能性あり s = 10
Case 1: Tのマッチング位置の文字 情報からスキップ幅決定 その文字が現れるところまでずらす パターン中に現れる文字で間違えたら? ⇒ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g n e v e r s = 5 s = 6 1つずつずらしていった とき, n が初めて出現するところまでSkip “n”のせいでマッチ しないことが明らか s = 7 s = 8 Skip n n n n n a n a e e e e l e l v v g g v v v o e e o e e e r l r r r r s = 9 マッチの可能性あり(n が初めて出現) いくつSkipしてよいか? の情報 ⇒ 全ての文字について前処理で計算しておいて, テーブルに保存
スキップ幅の計算 for 任意のアルファベットc skip[c]=m; for skip[P[i]]=m-i; a n l e g v o e l r 2文字目以降でミスした時用
Case 2: 2文字目以降でミスした時 • 「4文字目で間違った」という事実からわかること • Tの後ろ3文字はa,b,c • Tの後ろから4文字目はXではない Pのみでわかる x 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 z c a b c b a
Case 2: 2文字目以降でミスした時 • 「k文字目で間違った」という事実からわかること • Tの後ろk-1文字 • Tの後ろからk文字目はP[m-k+1]ではない Pのみでわかる x 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 z c a b c b a いくつSkipしてよいか? の情報 ⇒ 全てのについて前処理で計算しておいて, テーブルに保存
Case 2: 2文字目以降でミスした時 Case 2a: 途中まで正解した部分と同じ文字列が現れる • 「途中まで正解部分」と, それの“分身”がそろう • 「途中まで正解部分」の1文字前と分身の1文字前は異なる • ようにスキップ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 z c x x Y Y Skip 途中まで正解 と同じ!! 途中まで正解 a a a a b b b b c c c c b a ここがXなら マッチ可能性なし
Case 2: 2文字目以降でミスした時Case 2aが成立しない時 Case 2b: パターンの末尾と先頭が同じ 末尾と先頭をそろえる 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 z c x x 末尾と同じ!! 先頭と同じ!! Skip b b c c a a b b c c b a
Case 2: 2文字目以降でミスした時 Case 2c: Case 2a にも 2b にも当てはまらない パターンの長さ分(m文字分)だけ skip させる 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 z c x x 途中まで正解した部分と同じ文字列は現れない a a b b c c b a Skip これらのアイデアに基づくと, 文字の比較回数が4n回に抑えられる (証明は難しいので省略)
疑似コード 計算量(文字比較回数)=4n 実用上はKMPより高速
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“algol”)=3 ハッシュ値が等しい H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する Ans. アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c ハッシュ値が等しいので, 1文字ずつ比較 一致したので,出力 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c ハッシュ値を計算 H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“algaa”)=0 ハッシュ値を計算 H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“algaa”)=0 ハッシュ値を比較 H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“algaa”)=0 ハッシュ値が等しくない ので, 1文字ずつの比較を スキップ H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“lgaal”)=0 ハッシュ値が等しくない ので, 1文字ずつの比較を スキップ H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“gaalg”)=6 ハッシュ値が等しくない ので, 1文字ずつの比較を スキップ H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“aalgo”)=3 ハッシュ値が等しい H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c ハッシュ値が等しいので, 1文字ずつ比較 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c ハッシュ値が等しいので, 1文字ずつ比較 一致しないので,次へ a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c H(“algol”)=3 ハッシュ値が等しい H(“algol”)=3 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c ハッシュ値が等しいので, 1文字ずつ比較 a a a l l l g g g o o o l l l パターン P :
Rabin-Karpアルゴリズム(アイデアだけ) ハッシュ(Hash)値を利用する アルゴリズム パターンPが現れるか調べる際に, 先にハッシュ値を比較し, 等しい場合にのみ1文字ずつ比較する Ans. アイデア s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 文字列 T : a l g a b g o l a h c ハッシュ値が等しいので, 1文字ずつ比較 一致したので,出力 a a a l l l g g g o o o l l l パターン P :