算法-哈希表
发表于更新于
字数总计:1.2k阅读时长:4分钟阅读量: 济宁
哈希表
1、 存储结构
把一个有非常大值域的 x 直接取模,由于数值很多,取完模之后可能发生冲突,所有有了下面两种处理冲突的方法。
1️⃣开放寻址法
2️⃣ 拉链法
2、字符串哈希方式
例题
维护一个集合,支持如下几种操作:
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
输入样例:
输出样例:
开放寻址法
一维数组的长度要开到题目的两到三倍,冲突概率很低。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <iostream> #include <cstring> using namespace std;
const int N = 200003, null = 0x3f3f3f3f;
int h[N];
int find(int x) { int t = (x % N + N) % N; while (h[t] != null && h[t] != x) { t ++ ; if (t == N) t = 0; } return t; }
int main() { memset(h, 0x3f, sizeof h); int n; scanf("%d", &n); while (n -- ) { char op[2]; int x; scanf("%s%d", op, &x); if (*op == 'I') h[find(x)] = x; else { if (h[find(x)] == null) puts("No"); else puts("Yes"); } } return 0; }
|
拉链法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <iostream> #include <cstring> using namespace std;
const int N = 100003;
int e[N], ne[N], h[N], idx;
void insert(int x) { int k = (x % N + N) % N; e[idx] = x; ne[idx] = h[k]; h[k] = idx ++ ; }
bool find(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 (find(x)) printf("Yes\n"); else printf("No\n"); } } return 0; }
|
1 2
| 头节点(索引为 h[k]) -> 节点1 -> 节点2 -> ... 新头节点(new_node) -> 原头节点 -> 节点1 -> 节点2 -> ...
|
哈希字符串
解题思路:
在哈希表中,要查找一个元素,能通过 哈希函数 计算出其对应的 哈希值,直接查询。
在理想的情况下,元素与其哈希值 一 一对应。
在字符串哈希中,对于给定的字符串 ( 子串 ) ,也能求出其对应的哈希值。
若两个字符串 ( 子串 ) 的 哈希值完全相同,则就能断定两个字符串 ( 子串 ) 完全相同。
字符串哈希具体实现
注意:① 不能映射成0 ② 假定Rp足够好,不存在冲突
题目
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1][l1,r1] 和 [l2,r2][l2,r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n 和 m ,表示字符串长度和询问次数。
第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m 行,每行包含四个整数 l1, r1, l2, r2,表示一次询问所涉及的两个区间。
注意,字符串的位置从 1 开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
1 ≤ n , m ≤ 10^5
输入样例:
1 2 3 4 5
| 8 3 aabbaabb 1 3 5 7 1 3 6 8 1 2 1 2
|
输出样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <iostream>
using namespace std;
const int N = 100010, P = 131; typedef unsigned long long ULL;
int n, m; char str[N]; ULL p[N], h[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; }
|