---
# System prepended metadata

title: Rolling Hash

---

# Rolling Hash

> [time=Fri, Aug 8, 2025 10:06 PM]

本文假設讀者知道什麼是Rolling Hash and Rabin-Karp algorithm。可以看這個影片教的很好 [Link](https://www.youtube.com/watch?v=5rrWmGBB0Xo&t=1542s)

## 筆記

### pair hash

要看題目會不會刻意構造碰撞。有些題目出得很賊，逼得人一定得用pair hash。
如果遇到這種情況，可以左邊取$p=1e9+7$，右邊取$q=1e9+9$

兩個都是質數，根據[中國剩餘定理](https://zh.wikipedia.org/zh-tw/%E4%B8%AD%E5%9B%BD%E5%89%A9%E4%BD%99%E5%AE%9A%E7%90%86)，相當於對$pq$這個超大的數取模，幾乎不會碰撞。

### 不取模

也可以直接不取模，用`unsigned long long`或`unsigned __int128`，讓它自然溢出，相當於對$2^{64}$或$2^{128}$取模。

:::info
C++中，`unsigned`型別的溢出是自然的，不會報錯。但是`signed`的溢出是Undefined behavior，嚴格(雞婆)的編譯器會給runtime error。
:::

:::danger
C++中各種associative containers 對`ull`有支援，對`uint128`沒有。
:::

## Luogu, P3370 【模板】[字串哈希](https://www.luogu.com.cn/problem/P3370)

直接套模板就好。

:::spoiler

```cpp=1
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
ull pows[1505];
ull base = 31;
int val(char c) {
    if ('a' <= c && c <= 'z') return (c-'a'+1);
    else if ('A' <= c && c <= 'Z') return 26 + (c-'A'+1);    
    return 52 + (c-'0');
}
ull shash(string& s) {
    ull ans = 0;
    for (char c : s) ans = ans*base + val(c);
    return ans;
} 
int main() {
    pows[0]=1;
    for (int i=1;i<1505;i++) pows[i] = pows[i-1]*base;
    int n; cin >> n;
    unordered_set<ull> has;
    for (int i=0;i<n;i++) {
        string s; cin >> s;
        has.emplace(shash(s));
    }
    cout << has.size();
}
```

:::

## Leetcode, [30. Substring with Concatenation of All Words](https://leetcode.com/problems/substring-with-concatenation-of-all-words/description/)

todo

## Leetcode, [3327. Check if DFS Strings Are Palindromes](https://leetcode.com/problems/check-if-dfs-strings-are-palindromes/description/)

難度分2454
解決完postorder的問題後，我們就只要解決
> 給定`s`，請在$O(1)$內回答`is s[l,r] palindromic?`

的問題，就AC了。

這一題`ull`自然溢出就可以。
以下這個代碼，速度上無情打敗98.04%
順帶一提，改成`uint128`變成打敗60%
:::spoiler

```cpp=1
using ull = unsigned long long;
bool inited = false;
ull pows[100005];
void init() {
    if (inited) return;
    inited = true;
    pows[0]=1;
    for (int i=1;i<100005;i++) pows[i]=31*pows[i-1];
}
class Solution {
public:
    vector<vector<int>> G;
    vector<pair<int,int>> semmp;
    string s;
    string d0;
    void dfs(int loc = 0) {
        semmp[loc].first = d0.size();
        for (int next : G[loc]) {
            dfs(next);
        }
        d0 += s[loc];
        semmp[loc].second = d0.size();
    }
    vector<bool> findAnswer(vector<int>& parent, string& s) {
        init();
        int n = parent.size();
        this->s = s;
        G.resize(n);
        semmp.resize(n);
        for (int i=1;i<n;i++) {
            G[parent[i]].emplace_back(i);
        }
        dfs();
        vector<bool> ans(n);
        vector<ull> pre(n+1), repre(n+1);
        for (int i=1;i<=n;i++) {
            pre[i] = pre[i-1]*base + (d0[i-1]-'a'+1);
            repre[i] = repre[i-1]*base + (d0[n-i]-'a'+1);
        }
        for (int i=0;i<n;i++) {
            int l = semmp[i].first, r = semmp[i].second-1;
            ull sh = pre[r+1]-pre[l]*pows[r-l+1];
            ull revsh = repre[(n-1-l)+1]-repre[n-1-r]*pows[r-l+1];
            ans[i] = sh==revsh;
        }
        return ans;
    }
};

```

:::