知識概覽(哈希表)
- 哈希表可以將一些值域較大的數映射到較小的空間內,通常用x mod 質數的方式進行映射。為什么用質數呢?這樣的質數還要離2的整數冪盡量遠。這可以從數學上證明,這樣沖突最小。
- 取余還是會出現沖突情況。怎么解決沖突呢,有兩種方式:開放尋址法和拉鏈法。
- 算法題中哈希表的題目可能會有添加、查找操作,刪除操作較少,刪除用邏輯刪除,即用一個bool數組來標識出哪些數已經被刪除了。
例題展示
題目鏈接
https://www.acwing.com/problem/content/842/
代碼(拉鏈法)
#include <iostream>
#include <cstring>using namespace std;const int N = 100010;int h[N], e[N], ne[N], idx;void insert(int x)
{int k = (x % N + N) % N;e[idx] = x;ne[idx] = h[k];h[k] = idx++;
}bool query(int x)
{int k = (x % N + N) % N;for (int i = h[k]; i != -1; i = ne[i])if (e[i] == x)return true;return false;
}int main()
{int n;scanf("%d", &n);memset(h, -1, sizeof h);while (n--){char op[2];int x;scanf("%s%d", op, &x);if (*op == 'I') insert(x);else{if (query(x)) puts("Yes");else puts("No");}}return 0;
}
代碼(開放尋址法)
#include <iostream>
#include <cstring>using namespace std;const int N = 200003, null = 0x3f3f3f3f; // 數組長度設置為題目數據范圍的2~3倍且是質數int h[N];int find(int x)
{int k = (x % N + N) % N;while (h[k] != null && h[k] != x){k++;if (k == N) k = 0;}return k;
}int main()
{int n;scanf("%d", &n);memset(h, 0x3f, sizeof h);while (n--){char op[2];int x;scanf("%s%d", op, &x);int k = find(x);if (*op == 'I') h[k] = x;else{if (h[k] != null) puts("Yes");else puts("No");}}return 0;
}
知識概覽(字符串哈希)
- 字符串哈希也稱為字符串前綴哈希法,它先預處理出所有前綴的哈希值。
- 主要思想是用一個P進制的角度把一個字符串看成一個數字。例如一個字符串"ABCD",假設A為1,B為2,C為3,D為4,則其哈希值為
,其中P可以取131或13331,Q可以取
,這些是經驗值,99.99%的情況下不會出現沖突,不解決沖突。
- 字符串哈希用來快速判斷兩個字符串是不是相等。KMP算法可以求循環節,除此之外,KMP算法不如字符串哈希,字符串哈希確實簡單直接。
例題展示
題目鏈接
https://www.acwing.com/problem/content/843/
題解
不用考慮取余,溢出相當于取余。
代碼
#include <iostream>using namespace std;typedef unsigned long long ULL;const int N = 100010, P = 131;int n, m;
char str[N];
ULL h[N], p[N];ULL get(int l, int r)
{return h[r] - h[l - 1] * p[r - l + 1];
}int main()
{scanf("%d%d%s", &n, &m, str + 1);p[0] = 1;for (int i = 1; i <= n; i++){p[i] = p[i - 1] * P;h[i] = h[i - 1] * P + str[i];}while (m--){int l1, r1, l2, r2;scanf("%d%d%d%d", &l1, &r1, &l2, &r2);if (get(l1, r1) == get(l2, r2)) puts("Yes");else puts("No");}return 0;
}