Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

ローリングハッシュ(Rolling Hash)は、文字列の部分一致の高速比較重複検出などに使われるハッシュテクニックです。特に、「部分文字列が等しいか?」というクエリを O(1) 時間で処理するのに非常に効果的です。


✅ 目的と使用場面

目的:

文字列 S の区間 S[l..r]S[c..d] が一致するかを 高速に判定したい

使用例:

  • 文字列検索(Rabin-Karp法)
  • 部分文字列一致判定(今回の問題)
  • 重複検出(例:最長重複部分文字列)
  • データの同一性検証(例:データ同期やセキュリティ)

📘 仕組みの基本:多項式ハッシュ

文字列を数値に変換し、多項式として扱うことで、全体や部分の「ハッシュ値」を求めます。

例:文字列 abc を BASE=31 でハッシュ化

'a' = 1, 'b' = 2, 'c' = 3
H("abc") = 1*31² + 2*31¹ + 3*31⁰ = 1*961 + 2*31 + 3 = 961 + 62 + 3 = 1026

🔁 ローリングの意味:1文字ずつ拡張

連続した文字列のハッシュを O(1) で更新できる

hash[i+1] = hash[i] * BASE + S[i+1]

→ 前のハッシュから1文字だけ足して次のハッシュが作れる (なので「ローリング(転がる)」と呼ばれる)


🧮 前処理ステップ

Step 1: 文字を数値に変換

'a'  1, 'b'  2, ..., 'z'  26

Step 2: prefix hash を計算

hash[i] = S[0]*BASEⁿ⁻¹ + S[1]*BASEⁿ⁻² + ... + S[i-1]*BASE⁰

Step 3: power[i] = BASE^i を事前に用意

→ 後で部分ハッシュ抽出に使う


✂️ 区間のハッシュを O(1) で取り出す方法

問題:

prefix hash は S[0..i] のみを持っている → 任意の区間 S[l..r] を取り出したい

解法:

getHash(l, r) = (hash[r] - hash[l-1] * BASE^(r-l+1)) % MOD

図で表すと:

S:     a  b  c  d  e
idx:   1  2  3  4  5

hash[5] = hash of "abcde"
hash[2] = hash of "a"

S[3..5] = "cde" = hash[5] - hash[2] * BASE^3

✅ なぜ MOD を使うの?

問題:

  • ハッシュ値が非常に大きくなる
  • オーバーフローや衝突を避ける必要がある

解決:

  • 素数MOD(例:10^9 + 7)で制限し、衝突率も下げる
hash = (hash[i - 1] * BASE + value) % MOD;

🧠 衝突の問題と対策

  • 異なる文字列が同じハッシュ値になる「衝突」がまれに発生する
  • 対策:
    • MODやBASEを工夫する(大きな素数)
    • 二重ハッシュ:別のMOD・BASEで2組のハッシュを比較する

⏱️ 計算量まとめ

処理 時間
ハッシュ構築 O(N)
power構築 O(N)
部分ハッシュ取得 O(1)
比較クエリ処理 O(1) × Q

🔚 まとめ

項目 内容
基本操作 多項式ハッシュによる区間比較
キー関数 getHash(l, r) = hash[r] - hash[l-1]*BASE^len
ローリングの利点 文字列追加・削除に強い、部分一致が速い
使いどころ 重複検出、文字列比較、Karp-Rabinなど

✏️ 補足:応用例

  • 最長共通部分文字列を求める(バイナリサーチ + ローリングハッシュ)
  • Plagiarism 検出(類似文比較)
  • ファイル差分検出(rsyncアルゴリズム)