Skip to content

Commit

Permalink
ローリングハッシュ (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
ei1333 authored Sep 23, 2024
1 parent b26af9b commit 6cd59e6
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 70 deletions.
152 changes: 141 additions & 11 deletions docs/rolling-hash.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,150 @@
---
title: Rolling Hash (ローリングハッシュ)
documentation_of: //string/rolling-hash.hpp
---

## 概要
文字列に対応するハッシュを mod $2^{61}-1$ で計算します。文字列 $S$ のハッシュは以下の計算式で求めます。

文字列の一致判定を mod $2^{61}-1$ のハッシュを用いて効率的に行う.
$\mathrm{hash}(S) = \displaystyle \sum_{i=0}^{N-1} S_i b^i \pmod {2^{61} - 1}$

* `RollingHash(base)`: 基数 `base` のローリングハッシュを構築する.
* `generate_base()`: $2^{61} - 1$ 以下の乱数を返す。これを `base` とすると Hack されにくい.
* `build(s)`: 文字列 `s` のハッシュテーブルを返す.
* `query(s, l, r)`: `s` の区間 $[l, r)$ のハッシュ値を返す.
* `combine(h1, h2, h2len)`: ハッシュ値 `h1` と長さ `h2len` のハッシュ値 `h2` を結合する. `power[h2len]` での配列外参照に注意(TODO 実装).
* `lcp(a, l1, r1, b, l2, r2)`: ハッシュテーブル `a` の区間 $[l1, r1)$ と, ハッシュテーブル `b` の区間 $[l2, r2)$ の文字列の最長共通接頭辞の長さを返す.

# コンストラクタ

```cpp
(1) RollingHash()
(2) RollingHash(const string& s)
(3) RollingHash(const vector<T>& s)
```

1. 空文字列で初期化します。
2. `s` で初期化します。
3. `s` で初期化します。

## 計算量

- $O(n)$

# size

```cpp
int size() const
```

文字列の長さを返します。

## 計算量

- $O(1)$

# push_front

```cpp
void push_front(T c)
```
文字列の先頭に文字 `c` を追加します。
## 計算量
- $O(1)$
# push_back
```cpp
void push_back(T c)
```

文字列の末尾に文字 `c` を追加します。

## 計算量

- $O(1)$

# operator[]

```cpp
T operator[](int k) const
```

$k$ 番目 (0-indexed) の文字を返します。

## 制約

- $0 \leq k \lt n$

## 計算量

- $O(1)$

# get

```cpp
(1) mint get(int r) const
(2) mint get(int l, int r) const
```
1. $S[0, r)$ に対応するハッシュを返します。
2. $S[l, r)$ に対応するハッシュを返します。
## 制約
- $0 \leq l \leq r \leq n$
## 計算量
- $O(1)$
# lcp
```cpp
(1) int lcp(const RollingHash& b) const
(2) int lcp(const RollingHash& b, int l1, int l2) const
```

1. 自身の文字列と、`b` に対応する文字列の LCP の長さを返します。
2. 自身の文字列の `l1` 文字目以降と、`b` に対応する文字列の `l2` 文字目以降の文字列の LCP の長さを返します。

## 制約

- $l1 \leq n$
- $l2 \leq m$

## 計算量

- $O(\log n)$

# combine

```cpp
static mint combine(mint h1, int h1_len, mint h2)
```
長さ `h1_len` のハッシュ `h1` とハッシュ `h2` をこの順で結合したハッシュを返します。
## 計算量
- $O(1)$
# clear
```cpp
void clear()
```

文字列と対応するハッシュを空にします。

## 計算量

- $O(n)$

# merge

```cpp
void merge(RollingHash& b)
```
自身と `b` をこの順で結合します。副作用として `b` を空文字列にします。
## 計算量
* `build()`: $O(n)$
* `lcp()`: $O(\log n)$
* クエリ: $O(1)$
- $O(\min(n, m))$
76 changes: 76 additions & 0 deletions math/combinatorics/modint-2-61m1.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
struct ModInt_2_61m1 {
private:
using mint = ModInt_2_61m1;
using u64 = uint64_t;
using u128 = __uint128_t;

u64 x;

public:
ModInt_2_61m1() : x{} {}

explicit ModInt_2_61m1(u64 a) : x{a} {}

mint &operator+=(const mint &p) {
if ((x += p.x) >= mod()) x -= mod();
return *this;
}

mint &operator-=(const mint &p) {
if ((x += mod() - p.x) >= mod()) x -= mod();
return *this;
}

mint &operator*=(const mint &p) {
u128 c = (u128)x * p.x;
x = u64(c >> 61) + u64(c & mod());
if (x >= mod()) x -= mod();
return *this;
}

mint &operator/=(const mint &p) {
*this *= p.inv();
return *this;
}

mint operator-() const { return mint() - *this; }

mint operator+(const mint &p) const { return mint(*this) += p; }

mint operator-(const mint &p) const { return mint(*this) -= p; }

mint operator*(const mint &p) const { return mint(*this) *= p; }

mint operator/(const mint &p) const { return mint(*this) /= p; }

bool operator==(const mint &p) const { return x == p.x; }

bool operator!=(const mint &p) const { return x != p.x; }

u64 val() const { return x; }

mint pow(u64 n) const {
mint ret(1), mul(*this);
while (n > 0) {
if (n & 1) ret *= mul;
mul *= mul;
n >>= 1;
}
return ret;
}

mint inv() const { return pow(mod() - 2); }

friend ostream &operator<<(ostream &os, const mint &p) {
return os << p.val();
}

friend istream &operator>>(istream &is, mint &a) {
u64 t;
is >> t;
a = mint(t);
return is;
}

static constexpr u64 mod() { return (1ull << 61) - 1; }
};
Loading

0 comments on commit 6cd59e6

Please sign in to comment.