模拟哈希表&字符串哈希

模拟哈希表

引入

哈希表就是根据一个关键值key进行高效访问的数据结构,可以通过哈希函数把一个数据当做key进行映射得到一个储存地址从而进行访问。

比如想要查询100个数字范围在(1 ~ 1e8),查询它们是否有重复的值,那么就可以用哈希表来解决这个问题。

对于小的数字我们就会习惯的会去一个数组进行标记,但是对于大的数字,开一个很大数组用来标记是很浪费空间,而且要处理的数只有100个,开一个1e8的数组实在是划不来,而且当数据再增大时,对于C++可能就无法直接开出这样的数组。

哈希函数

此时就可以对此些数字进行转换,比如,我们可以对其进行取模,这样就可以把他们都转化成一定范围的数字。
这种把这些数据按照一定方式进行转化的函数就称之为哈希函数,上面所使用的哈希函数就是取模。

处理冲突

可以发现用哈希函数处理之后,可能存在相同的值,比如5 % 3 = 2, 8 % 3 = 2这样就可能会使查询的结果错误,那么就需要用一种方式处理冲突了(可以证明当模数取质数时冲突几率最小)。

拉链法

拉链法处理冲突很简单,对于几个重复的值可以把他们放在一个链表里,查询时只需要去翻查链表就可以知道是否存在了

using namespace std;
const int mod = 100003;
int h[100005],cnt = 1,n;
struct node{
    
    
    int nex,number;
}edge[100005];//链表
void insert(int x){
    
    
    int hashCode = (x % mod + mod) % mod;//把负数也给映射过来
    edge[cnt].number = x;
    edge[cnt].nex = h[hashCode];
    h[hashCode] = cnt++;
}
bool find(int x){
    
    
    int hashCode = (x % mod + mod) % mod;
    for(int i = h[hashCode]; i != 0;i = edge[i].nex){
    
    //遍历该哈希值下的所有数
        if(edge[i].number == x)return true;
    }
    return false;
}

字符串哈希

字符串的哈希可以采用前缀哈希(在竞赛中)
对于一个字符串可以把它当成一个p进制的数字,比如用sum来表示前缀对于字符串:“acf”
那么就有:
s u m [ 1 ] = P ∗ ‘ a ’ sum[1] = P * ‘a’ sum[1]=Pa
s u m [ 2 ] = P 2 ∗ ‘ c ’ + s u m [ 1 ] sum[2] = P^2 * ‘c’ + sum[1] sum[2]=P2c+sum[1]
s u m [ 3 ] = P 3 ∗ ‘ f ’ + s u m [ 2 ] sum[3] = P^3 * ‘f’ + sum[2] sum[3]=P3f+sum[2]
但是这样就会有一个问题,最后求出来的数字很大,所以就需要进行取模。然后就可以完成前缀hash了。
对于P一般取131,对于模数一般取 2 64 2^{64} 264这样开unsigned long long进位时就会自动取模了,对于重复的字符串,重复的几率很小,所以几乎可以当做不重复,如果相求某一段字符串的哈希值那么就可以用 s u m [ r ] − s u m [ l − 1 ] ∗ P r − l + 1 sum[r] - sum[l - 1] * P^{r - l + 1} sum[r]sum[l1]Prl+1这个公式求得
题目: 字符串hash

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define eps 1e-8
using namespace std;
const ll maxn = 1e5 + 5;
typedef unsigned long long ull;
int n,m,L1,R1,L2,R2;
ull p[maxn],sum[maxn];
char str[100005];
ull finds(int l,int r){
    
    
    ull res = sum[r] - sum[l - 1] * p[r - l + 1];
    return res;
}
int main(){
    
    
    scanf("%d %d",&n,&m);
    scanf("%s",str + 1);
    p[0] = 1;
    for(int i = 1; i <= n; i++){
    
    
        p[i] = p[i - 1] * 131;
        sum[i] = sum[i - 1] * 131 + str[i];
    }
    while(m--){
    
    
        scanf("%d %d %d %d",&L1,&R1,&L2,&R2);
        if(finds(L1,R1) == finds(L2,R2))
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36102055/article/details/107409183