1.模拟哈希表
哈希表的核心就是哈希算法,将一个数经过哈希算法计算出哈希值,这样避免不了哈希冲突,哈希冲突就是不同的元素经过哈希算法计算出的哈希值是相同的,我们必须同时保存这些元素,解决哈希冲突主要有两种方式
-
拉链法
-
开放寻址法
1.1拉链法
拉链法就是在一个位置上如果有多个元素,那么这些元素用链表的形式连起来,HashMap解决哈希冲突使用的就是拉链法,只不过它使用的是尾插法,这里我们只是用简单的数组来实现的,使用的是头插法
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static final int N = 100003;
//哈希表 存的是每个位置的链表的头结点的索引
static int[] h = new int[N];
//data域
static int[] e = new int[N];
//next域 存下一个节点的索引
static int[] ne = new int[N];
static int idx;
//简单实现哈希算法 这里的算法是因为题目里的数据有负数,为了计算出的哈希值都是正数
private static int hash(int x) {
return (x % N + N) % N;
}
//头插法 最后一个节点的ne是-1
private static void set(int x) {
int k = hash(x);
e[idx] = x;
//新节点的next域指向头节点
ne[idx] = h[k];
//更新头结点
h[k] = idx++;
}
private static boolean get(int x){
int k = hash(x);
for(int i = h[k];i != -1;i = ne[i]){
if(e[i] == x){
return true;
}
}
return false;
}
public static void main(String[] args) {
//初始化h数组 即所有的位置都没有数据 并且可以保证每个位置的链表的最后一个节点的ne==-1 作为终止条件
Arrays.fill(h, -1);
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
while (n-- > 0) {
String str = scanner.next();
int x = scanner.nextInt();
if ("I".equals(str)) {
set(x);
} else {
if(get(x) == true) System.out.println("Yes");
else System.out.println("No");
}
}
}
}
1.2开放寻址法
开放寻址法就是开一个比元素个数多1到2倍长度的数组,元素不同但是哈希值相同的元素(哈希冲突)放到该位置的下一个位置,如果下一个位置还有元素,继续放到下一个位置。。。,不像拉链法一样以链表的形式存储。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static final int N = 200003;
static int idx;
static int[] h = new int[N];
static int max = Integer.MAX_VALUE;
private static int hash(int x) {
return (x % N + N) % N;
}
//寻找x的位置或者是x应该插入的位置
private static int find(int x) {
int k = hash(x);
//当这个位置上不是max或者不是查询的数据
while(h[k] != max && h[k] != x){
//继续走
k ++;
//当走到末尾时 从头走
if(k == N) k = 0;
}
//最终的k可能是x的索引位置 或者是此位置没有元素时就是x的插入位置
return k;
}
public static void main(String[] args) {
//初始化所有的数据 都在元素的范围之外
Arrays.fill(h, max);
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
while (n-- > 0) {
String str = scanner.next();
int x = scanner.nextInt();
int idx = find(x);
if ("I".equals(str)) {
h[idx] = x;
} else {
if(h[idx] != max) System.out.println("Yes");
else System.out.println("No");
}
}
}
}
2.字符串哈希
字符串哈希就是将一个字符串看做是一个10进制的数,将其转换为P进制的数(每次%(2^64)
)保证这个数在2^64内。(P一般取 31 131 13331)
,整个字符串看做是一个十进制的数,我们将这个十进制的数转换成P进制的数就是字符串哈希的过程。
主要的用途用于解决字符串的匹配问题,注意 : 这种方式求得的哈希值99.999%没有哈希冲突,所以不考虑哈希冲突
此题就是判断一个字符串中两个子串的内容是否相等,我们就可以使用字符串哈希的方式,分别计算出这两段串的哈希值然后比较即可
求某一段的哈希值,我们可以先预处理一个字符串前缀哈希值,例如一个串 abcdefgh,我们先求出a ab abc abcd 。。。的哈希值
然后将求 l到r段的串的哈希值,只需要将h[r] - h[l - 1] * p[r - l +1]即可
即将h[l - 1]与h[r]对齐(需要在P进制的情况下左移,所以这里乘p[r - l + 1])
然后相减就是 l 到r 的哈希值
所以核心就是
1、预处理1-n子串的哈希
2、公式获取区间的哈希值
import java.util.Scanner;
public class Main {
static final int P = 131;
static final int N = 100010;
static long[] h = new long[N];
static long[] p = new long[N];
//将h[l - 1] 与 h[r]对其 将h[l - 1]左移 然后相减 就是 l 到 r 之间的字符串的哈希值
static long hash(int l, int r) {
return h[r] - h[l - 1] * p[r - l + 1];
}
public static void main(String[] args) {
p[0] = 1;
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
String str = scanner.next();
//求前1-n个元素的哈希值
for (int i = 1; i <= n; i++) {
h[i] = ((h[i - 1] * P) + str.charAt(i - 1)) % Long.MAX_VALUE;
//这里就是为了hash()方法时 完成类似二进制左移的功能
p[i] = p[i - 1] * P;
}
while (m-- > 0) {
int l1 = scanner.nextInt();
int r1 = scanner.nextInt();
int l2 = scanner.nextInt();
int r2 = scanner.nextInt();
if (hash(l1, r1) == hash(l2, r2)) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
}