06-数学相关/05-字符串哈希

来自三三百科
->Importer2026年5月20日 (三) 16:50的版本 (批量导入三三文档)
跳转到导航 跳转到搜索

整串哈希

单哈希

const long long MOD = 1000000007;
const int P = 29;

long long getHash(const string &s)
{
    long long hash = 0;
    for (int i = 0; i < s.length(); i++)
        hash = (hash * P + s[i]) % MOD;
    return hash;
}

双哈希(更安全)

const long long MOD1 = 1000000007, MOD2 = 998244353;
const int P1 = 29, P2 = 31;

pair<long long, long long> getHash(const string &s)
{
    long long hash1 = 0, hash2 = 0;
    for (int i = 0; i < s.length(); i++)
    {
        hash1 = (hash1 * P1 + s[i]) % MOD1;
        hash2 = (hash2 * P2 + s[i]) % MOD2;
    }
    return {hash1, hash2};
}

自然溢出哈希(简便)

const int P = 29;

unsigned long long getHash(const string &s)
{
    unsigned long long hash = 0;
    for (int i = 0; i < s.length(); i++)
        hash = hash * P + s[i]; // 自动对 2^64 取模
    return hash;
}

子串哈希

通过预处理前缀哈希,可以在 [math]\displaystyle{ O(1) }[/math] 时间内获取任意子串的哈希值。

string s; // s[1]~s[n]
const long long MOD = 1000000007;
const int P = 29;
long long myHash[30005];
long long powP[30005];

void initHash(int n)
{
    powP[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        myHash[i] = (myHash[i - 1] * P + s[i] - 'a' + 1) % MOD;
        powP[i] = powP[i - 1] * P % MOD;
    }
}

// 获取 s[l]~s[r] 的哈希值
int getHash(int l, int r)
{
    return (myHash[r] - myHash[l - 1] * powP[r - l + 1] % MOD + MOD) % MOD;
}

循环节判断

如果 s[1 ~ n-len] == s[len+1 ~ n],那么 s[1~len] 是一个循环节。

可以用子串哈希快速判断两个子串是否相等。

字符串去重

unordered_set<unsigned long long> hashes;
for (int i = 0; i < n; i++)
{
    string s;
    cin >> s;
    hashes.insert(getHash(s));
}
cout << hashes.size(); // 不同字符串的数量