【蓝桥杯集训8】哈希表专题(3 / 3)

目录

手写哈希表

1、开放寻址法

2、拉链法

字符串前缀哈希表法

2058. 笨拙的手指 - 哈希表 + 秦九韶算法(进制转换)+ 枚举

秦九韶算法——将x进制数转化为十进制数


手写哈希表

活动 - AcWing

1、开放寻址法

设 h(x)=k,也就是 x 的哈希值为 k

如果在 hash[k]的位置已经有元素,持续往后遍历直到找到 >x(询问)或为空(插入)为止

注意开放寻址法一般会把数组开到数据范围的 2−3 倍,能提高效率

import java.util.*;

class Main
{
    static int N=200003,nul=0x3f3f3f3f; //质数
    static int[] h=new int[N];
    
    public static int find(int x)
    {
        int k=(x%N+N)%N;
        
        while(h[k]!=nul&&h[k]!=x) //如果该坑位不为空且该坑位不为x(如果x已经存过就不存了)
        {
            k++;
            if(k==N) k=0;
        }
        return k; //如果找到x,则返回x所在的位置
                  //如果没有找到x,则返回x应该存放的位置
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        
        Arrays.fill(h,nul);
        
        while(n-->0)
        {
            String s=sc.next();
            int x=sc.nextInt();
            
            int k=find(x);
            
            if(s.equals("I")) h[k]=x;
            else if(s.equals("Q"))
            {
                if(h[k]!=nul) System.out.println("Yes");
                else System.out.println("No");
            }
        }
    }
}

2、拉链法

设 h(11)=3,h(23)=3 ,这就是一种冲突

我们可以设一个数组h,也就是哈希的结果

对于每一个结果k,建立一个链表

把映射为 k 的所有数 x 都放在 h[k] 这个链表里

import java.util.*;

class Main
{
    static int N=100003; //质数
    static int[] h=new int[N],e=new int[N],ne=new int[N];
    static int idx;
    
    public static void insert(int x)
    {
        int k=(x%N+N)%N; //+N%N是为了让负数变成正数
        e[idx]=x;
        ne[idx]=h[k];
        h[k]=idx++;
    }
    
    public static boolean 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;
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        
        Arrays.fill(h,-1);
        
        while(n-->0)
        {
            String s=sc.next();
            int x=sc.nextInt();
            
            if(s.equals("I")) insert(x);
            else if(s.equals("Q"))
            {
                if(find(x)) System.out.println("Yes");
                else System.out.println("No");
            }
        }
    }
}

 

字符串前缀哈希表法

把字符串变成一个p进制数字(哈希值),实现不同的字符串映射到不同的数字

对形如 X_{1}X_{2}X_{3}......X_{n-1}X_{n} 的字符串,采用字符的ascii 码乘上 P 的次方来计算哈希值

映射公式 (X_{1}\times P^{n-1}+X_{2}\times P^{n-2}+......+X_{n-1}\times P^{1}+X_{n}\times P^{0})mod\ Q

注意:

  • 任意字符不可以映射成0,否则会出现不同的字符串都映射成0的情况,比如A,AA,AAA皆为0
  • 通过巧妙设置P (13113331) , Q (2^{64})的值,一般可以理解为不产生冲突

比较不同区间的子串是否相同,就转化为对应的哈希值是否相同

求一个字符串的哈希值就相当于求前缀和,求一个字符串的子串哈希值就相当于求部分和

字符串前缀和公式:

h[i] = h[i-1]*P + s[i];

区间和公式:h[l,r]=h[r]-h[l]*P^{r-l+1}

区间和公式的理解: ABCDE 与 ABC 的前三个字符值是一样,只差两位,
乘上 P^{2} 把 ABC 变为 ABC00,再用 ABCDE - ABC00 得到 DE 的哈希值

活动 - AcWing

题目:

import java.util.*;

class Main
{
    static int N=100010;
    static long[] h=new long[N],p=new long[N];
    static int P=131;
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),m=sc.nextInt();
        String s="/"+sc.next();//下标从1开始
        
        p[0]=1;
        for(int i=1;i<=n;i++)
        {
            p[i]=p[i-1]*P;
            h[i]=h[i-1]*P+s.charAt(i);
        }
        
        while(m-->0)
        {
            int l1=sc.nextInt(),r1=sc.nextInt();
            int l2=sc.nextInt(),r2=sc.nextInt();
            long h1=h[r1]-h[l1-1]*p[r1-l1+1];
            long h2=h[r2]-h[l2-1]*p[r2-l2+1];
            System.out.println(h1==h2?"Yes":"No");
        }
    }
}

2058. 笨拙的手指 - 哈希表 + 秦九韶算法(进制转换)+ 枚举

2058. 笨拙的手指 - AcWing题库

题目:

第一行给你一个N的二进制表示,其中有一位是错误的

第二行给你一个N的三进制表示,其中有一位是错误的

输出N的正确十进制值(题目保证一定有唯一解)

思路:

枚举二进制数a的所有换1位的情况,转化为十进制值存入哈希表

枚举三进制数b的所有换1位情况,转化为十进制后在哈希表中查询是否存在 

因为题目保证一定有唯一解,所以两者交集即为答案

因此如果查询到,则输出

秦九韶算法——将x进制数转化为十进制数

public static int get(String s,int x)
{
    int res=0;
    for(int i=0;i<s.length();i++)
    {
        char t=s.charAt(i);
        res=res*x + t-'0';
    }
    return res;
}
import java.util.*;

class Main
{
    static int N=100010;
    
    public static int get(char[] s,int x)//将x进制数s转化为十进制数
    {
        //秦九韶算法
        int res=0;
        for(int i=0;i<s.length;i++)
        {
            res=res*x+s[i]-'0';
        }
        return res;
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        char[] a=sc.next().toCharArray();
        char[] b=sc.next().toCharArray();
        Set<Integer> st=new HashSet<>();
        
        for(int i=0;i<a.length;i++)
        {
            a[i]^=1;
            st.add(get(a,2));
            a[i]^=1;
        }
        
        for(int i=0;i<b.length;i++)
        {
            char t=b[i];
            for(int j=0;j<3;j++) //将这一位换成除了本身的其他数
                if(t-'0'!=j)
                {
                    b[i]=(char)(j+'0');
                    int tp=get(b,3);
                    if(st.contains(tp))
                    {
                        System.out.print(tp);
                        return;
                    }
                }
            b[i]=t; //换完该位要恢复现场 继续换下一位
        }
    }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值