哈希表
First Post:
Last Update:
本章无需前置知识
哈希表
哈希表之前有讲过,就不再举例子了,课上将直接讲解
俩种解决哈希冲突的方法
例题:
维护一个集合,支持如下几种操作:
I x,插入一个数 x;
Q x,询问数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N,表示操作数量。
接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。
输出格式
对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤N≤10^5
−10^9≤x≤10^9
输入样例:
输出样例:
拉链法
#include <iostream> #include <cstring>
using namespace std;
const int N = 1e5 + 10;
int h[N], e[N], ne[N], idx;
int ha(int x) { return (x % N + N) % N; }
void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ; }
void insert(int x) { int t = ha(x); add(t, x); }
bool query(int x) { int t = ha(x); for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (j == x) return true; } return false; }
int main() { int T; cin >> T; memset(h, -1, sizeof h); while (T -- ) { char op; int x; cin >> 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 = 2e5 + 10;
int h[N], null = 0x3f3f3f3f;
int ha(int x) { return (x % N + N) % N; }
void insert(int x) { int t = ha(x); while (h[t] != null && h[t] != x) { t = (t + 1) % N; } h[t] = x; }
bool query(int x) { int t = ha(x); while (h[t] != x && h[t] != null) { t = (t + 1) % N; } return h[t] == x; }
int main() { int T; cin >> T; memset(h, 0x3f, sizeof h); while (T -- ) { char op; int x; cin >> op >> x; if (op == 'I') insert(x); else { if (query(x)) puts("Yes"); else puts("No"); } } return 0; }
|
字符串哈希
全称字符串前缀哈希法,把字符串变成一个p进制数字(哈希值),实现不同的字符串映射到不同的数字。
对形如 X1X2X3⋯Xn−1Xn 的字符串,采用字符的ascii 码乘上 P 的次方来计算哈希值。
映射公式 (X1×Pn−1+X2×Pn−2+⋯+Xn−1×P1+Xn×P0)modQ
注意点:
- 任意字符不可以映射成0,否则会出现不同的字符串都映射成0的情况,比如A,AA,AAA皆为0冲突问题:
- 通过巧妙设置P (131 或 13331) , Q (2^64)的值,一般可以理解为不产生冲突。
例题:
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1]和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n 和 m,表示字符串长度和询问次数。
第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m 行,每行包含四个整数 l1,r1,l2,r2,表示一次询问所涉及的两个区间。
注意,字符串的位置从 1 开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤n,m≤10^5
输入样例:
8 3 aabbaabb 1 3 5 7 1 3 6 8 1 2 1 2
|
输出样例:
#include <iostream> #include <algorithm>
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", &n, &m); scanf("%s", str + 1);
p[0] = 1; for (int i = 1; i <= n; i ++ ) { h[i] = h[i - 1] * P + str[i]; p[i] = p[i - 1] * P; }
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; }
|
我们数据结构学习到这里就告一段落了