数据结构_哈希表与字符串哈希

1.模拟哈希表

哈希表的核心就是哈希算法,将一个数经过哈希算法计算出哈希值,这样避免不了哈希冲突,哈希冲突就是不同的元素经过哈希算法计算出的哈希值是相同的,我们必须同时保存这些元素,解决哈希冲突主要有两种方式

  • 拉链法

  • 开放寻址法

1.1拉链法

拉链法就是在一个位置上如果有多个元素,那么这些元素用链表的形式连起来HashMap解决哈希冲突使用的就是拉链法,只不过它使用的是尾插法,这里我们只是用简单的数组来实现的,使用的是头插法

AcWing.840模拟散列表

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%没有哈希冲突,所以不考虑哈希冲突

AcWing841.字符串哈希

此题就是判断一个字符串中两个子串的内容是否相等,我们就可以使用字符串哈希的方式,分别计算出这两段串的哈希值然后比较即可

求某一段的哈希值,我们可以先预处理一个字符串前缀哈希值,例如一个串 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");
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_46312987/article/details/120104984