洛谷題單指南-集合-P3370 【模板】字串雜湊

江城伍月發表於2024-03-20

原題連結:https://www.luogu.com.cn/problem/P3370

題意解讀:本題要求理解雜湊的原理,自行建立雜湊表解題,如果直接使用map,就不推薦。

解題思路:

先介紹雜湊的原理

1、什麼是雜湊?什麼是雜湊表?

先從一個問題出發:給定不超過105個整數(取值1~109),要統計不重複整數的數量。

首先,如果取值範圍是1~105,可以藉助計數排序的思想,定義陣列int a[N],N在1e5規模,遍歷所有整數,對出現的整數標記a[i] = 1,最後統計>0的個數;

但是,由於取值範圍是1~109,如果定義陣列int a[N],N在1e9規模,記憶體會爆掉,能不能用更小的資料結構來搞定呢?

可以定義vector<int> a[M],M在1e5規模內即可

對於每一個整數i,先計算i % M,取模之後必然落到[0~M)區間,然後將i存入a[i % M].push_back(i)

在存入i之前,可以判斷a[i % M]這個vector裡是否已經存在了i,存在則不加入

最後,遍歷所有a[k],累加a[k].size()即得不重複的整數數量。

把一個大的整數對映到一個小整數的過程叫做雜湊,如i % M;

用來儲存元素的vector<int> a[M]則稱為雜湊表

2、字串如何雜湊?

字串也可以轉化為整數,只需把字串當做p進位制數來處理即可

例如:ABC,轉成數字:'A' * p2 + 'B' * p1 + 'C' * p0

再對一個合適的M取模:('A' * p2 + 'B' * p1 + 'C' * p0) % M

就完成了字串的雜湊

通常:P取一個素數會減少衝突,常見選擇如:131、13331

M則根據實際資料量進行選擇即可。

100分程式碼:

#include <bits/stdc++.h>
using namespace std;

const int N = 10005, M = 10000, P = 131;

vector<string> h[N];
int n, ans;
string s;

int main()
{
    cin >> n;
    while(n--)
    {
        cin >> s;
        int hashcode = 0;
        for(int i = 0; i < s.size(); i++)
        {
            hashcode = (hashcode * P + s[i]) % M; //類似二進位制轉十進位制的操作計算hashcode,每次都要%M以免溢位
        }
        bool exist = false; //s是否已經存在
        for(int i = 0; i < h[hashcode].size(); i++) //在h[hashcode]中查詢s是否存在
        {
            if(h[hashcode][i] == s)
            {
                exist = true;
                break;
            } 
        }
        if(!exist)
        {
            h[hashcode].push_back(s); //如果s不存在則新增
            ans++;
        } 
    }
    cout << ans;

    return 0;
}

相關文章