一.什么是哈希表呢?
散列表(也叫哈希表),是根据关键码-值而直接进行访问的数据结构。
哈希表的优点:查找数据速度快
二.存储结构:
把一个大的数据映射成一个小的数据
1.拉链法
2.开放寻址法
拉链法:用数组加链表来模拟
开放寻址法:用当前数据大小的两到三倍空间防止溢出模拟
如何写呢?
题目:维护一个集合,支持如下几种操作:
I x,插入一个数 x;
Q x,询问数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N,表示操作数量。
接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。
输出格式
对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤N≤105
−109≤x≤109
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No
1.拉链法
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 100003;
int n, x;
char op[2];
int h[N], e[N], ne[N], idx = 1;//链表中定义e[],ne[],idx;
void insert(int x) {
int k = (x % N + N) % N;//为什么要加N,为了防止k是负数,故要先取
//模再加再取模
e[idx] = x;//把x插到k号位置的链表中
ne[idx] = h[k];
h[k] = idx++;
}
bool query(int x) {
int k = (x % N + N) % N;//先找到对应k号下标
for (int i = h[k]; i; i = ne[i]) {//遍历k的链表
if (e[i] == x) {//如果找到对应的值,返回真
return true;
}
}
return false;
}
int main() {
scanf("%d", &n);
while (n--) {
scanf("%s%d", op, &x);//为了防止出题人加空格,所以定义用char [2]
if (op[0] == 'I') {
insert(x);
} else {
if (query(x))
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}
2.开放寻址法
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 300007, null = 0x3f3f3f3f;//这个数不在数据范围之内,如果有数据是这个,这个就是空的
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() {
memset(h, 0x3f, sizeof h);//先把所有数据都初始成最大值
int n;
cin >> n;
char op;
int x;
while (n--) {
cin >> op >> x;
int k = find(x);
if (op == 'I')
h[k] = x;
else {
if (h[k] != null)
cout << "Yes\n";
else
cout << "No\n";
}
}
return 0;
}
三. 字符串哈希
题目
给定一个长度为n的字符串,再给定m个询问,每个询问包含四个整数l1,r1,l2,r2,请你判断[l1,r1]和[l2,r2]这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数n和m,表示字符串长度和询问次数。
第二行包含一个长度为n的字符串,字符串中只包含大小写英文字母和数字。
接下来m行,每行包含四个整数l1,r1,l2,r2,表示一次询问所涉及的两个区间。
注意,字符串的位置从1开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出“Yes”,否则输出“No”。
每个结果占一行。
输入
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
输出
Yes
No
Yes
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, P = 131; // 这里的P 是经验值 131 ,或者13331
int n,m;
char str[N];
// 这里用unsigned long long 存储就相当于mod 2 ^ 64, 因为超过了会溢出的
ULL h[N], p[N]; // h[]是存储字符串哈希值的 p[] 是存储p次方的
ULL get(int l, int r)
{
return h[r] - h[l -1] * p[r - l + 1]; // 区间hash 的公式
}
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; // p数组保存 计算的次方数
h[i] = h[i-1] * P +str[i]; // 计算字符串的前缀, 后面的是0次 所以直接加上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)) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}