-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
348 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
}; |
Oops, something went wrong.