AcWing算法基础课 哈希表(持续更新中···)

哈希表( H a s h Hash Hash表、散列表)

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。

模板题链接:AcWing 840.模拟散列表

拉链法(用个链表来存储每个位置上的集合)

AC核心代码:要记得把h数组全部赋值为-1。这里N为大于100000的第一个质数,用质数来说相对比较好,能最大限度减少冲突

const int N = 1e5 + 3;
int h[N], e[N], ne[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(x == e[i])
            return true;
    }
    return false;
}

开放寻址法

核心就是先找个一个位置,如果这个位置上有数就往看下一个位置,直到找到没得数的位置。一般把数组开成原来的2-3倍大小。同样也要把N设置为第一个大于的质数,减少冲突
注意这道题不能把 h h h 数组赋值为全 0 0 0 ,因为 0 0 0 可能在数据集里面,应该全部赋值为一个永远用不到的数,比如 n u l l = 0 x 3 f 3 f 3 f 3 f null=0x3f3f3f3f null=0x3f3f3f3f
AC核心代码如下:

const int N = 2e5+3,null=0x3f3f3f3f;
int h[N];

void insert(int x)
{
    int k = (x % N + N) % N;
    while(true)
    {
        if(h[k]==null)
        {
            h[k] = x;
            break;
        }
        ++k;
    }
}

bool find(int x)
{
    int k = (x % N + N) % N;
    while(h[k]!=null)
    {
        if(h[k]==x)
            return true;
        ++k;
    }
    return false;
}

字符串哈希(这里介绍的是字符串前缀哈希)

对于一个字符串:" A B C A B C D E X A C W I N G ABCABCDEXACWING ABCABCDEXACWING"
有前缀哈希数组 h h h,则有
h [ 0 ] = 0 h[0]=0 h[0]=0
h [ 1 ] = h[1]= h[1]= A A A” 的哈希值
h [ 2 ] = h[2]= h[2]= A B AB AB” 的哈希值
h [ 3 ] = h[3]= h[3]= A B C ABC ABC” 的哈希值
h [ 4 ] = h[4]= h[4]= A B C A ABCA ABCA” 的哈希值
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ······
这里的 h h h 数组存储的是这个字符串的哈希值

比如" A B C D ABCD ABCD"这个字符串,看成是 p p p进制的数
A B C D A B C D ABCD
( 1234 ) P ( 1 2 3 4)_P (1234)P
= = = ( 1 ∗ p 3 + 2 ∗ p 2 + 3 ∗ p 1 + 4 ∗ p 0 ) % Q (1*p^3+2*p^2+3*p^1+4*p^0)\%Q (1p3+2p2+3p1+4p0)%Q
这样就把任何一个字符串映射成了 [ 0 , Q − 1 ] [0,Q-1] [0,Q1]之间的一个数
P S : PS: PS:
一般不能把一个字母映射成0,比如上面把A映射成0,那么ABCD和BCD的哈希值都是一样的了
经验值:当 P = 131 P=131 P=131或者 P = 13331 P=13331 P=13331 Q = 2 64 , 一 般 的 99.99 % 的 情 况 下 不 会 出 现 冲 突 , 冲 突 的 概 率 大 约 是 几 十 亿 分 之 一 Q=2^{64},一般的99.99\%的情况下不会出现冲突,冲突的概率大约是几十亿分之一 Q=26499.99%亿

模板题:AcWing 841.字符串哈希
这里我们直接用ULL,因为溢出相当于取模
可以先预处理p数组,之后查询的时候就非常快:
p [ i ] = p [ i − 1 ] ∗ P p[i]=p[i-1]*P p[i]=p[i1]P

预处理 h h h 哈希数组:
a r r [ i ] = a r r [ i − 1 ] ∗ p + s t r [ i ] arr[i]=arr[i-1]*p+str[i] arr[i]=arr[i1]p+str[i] 其 中 s t r [ i ] 只 要 不 为 0 就 行 其中str[i]只要不为 0 就行 str[i]0

得到一个字符串中的 [ l , r ] [l,r] [l,r]段子字符串的哈希值是:
a r r [ r ] − a r r [ l − 1 ] ∗ p [ r − l + 1 ] arr[r]-arr[l-1]*p[r-l+1] arr[r]arr[l1]p[rl+1]

AC代码如下:

#include<bits/stdc++.h>

using namespace std;
#define ULL unsigned long long
const int MAXN = 1e5 + 10;
int P = 131;
ULL arr[MAXN], p[MAXN];
int n, m, l1, r1, l2, r2;
char str[MAXN];


ULL get(int l, int r)
{
    return arr[r] - arr[l - 1] * p[r - l + 1];
}

int main()
{
    p[0] = 1;
    scanf("%d%d%s", &n, &m, str + 1);

    for(int i = 1; i <= n; ++i)
    {
        p[i] = p[i - 1] * P;
        arr[i] = arr[i - 1] * P + str[i];
    }

    while(m--)
    {
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        if(get(l1, r1) == get(l2, r2))
            printf("Yes\n");
        else
            printf("No\n");
    }

    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值