The Festival of Insign|

hsy2093

园龄:1年5个月粉丝:0关注:0

哈希表与字符串哈希

哈希表

哈希表:把相对复杂的数据(0109)集合映射到一个较小的数据集合中,通常映射到(0105)(0106),一般来说,有两种基础操作,添加和查找,实现的时间复杂度为O(1)

哈希函数所要考虑的

1)哈希函数的写法,如x mod 大质数

2)发生冲突时,应如何寻找值(开放寻址法、拉链法)

哈希表存储结构

1)开放寻址法
10
插入操作:从应放置的t位置开始判断,一旦有一个空的位置就将其插入。

查找操作:从从应放置的t位置开始搜索,如果遇到值则返回,若遇到第一个不为空的位置不等于查找的值,则查找不到该值。

注意事项:如果数据范围为105,那么开放寻址法的数组大小要开到原范围的2~3倍, 即3105

const int N = 200003, null = 0x3f3f3f3f;
int h[N];
//如果x在哈希表中已经存在,那么返回的是所在位置下标,如果不存在则返回当前应该在哈希表中存储的位置。
int find(int x)
{
    int t = (x % N + N) % N;
    while (h[t] != null && h[t] != x)
    {
        t ++ ;
        if (t == N) t = 0;
    }
    return t;
}

2)拉链法

用一个一维数组来存储所有的哈希值,如果有值发生冲突,则在原本位置连接新值。
10

//向哈希表中添加元素
const int N = 100003;
//h数组表示
int h[N], e[N], ne[N], idx;
//链表头插法
void insert(int x){
	//重要写法,因为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(e[i] == x)	return true;
	}
	return false;
}

字符串哈希

定义

我们定义一个把字符串映射到整数的函数f,这个f称为是 Hash 函数。

我们希望这个函数f可以方便地帮我们判断两个字符串是否相等。

性质

具体来说,哈希函数最重要的性质可以概括为下面两条:

  1. 在 Hash 函数值不一样的时候,两个字符串一定不一样;

  2. 在 Hash 函数值一样的时候,两个字符串不一定一样(但有大概率一样,且我们当然希望它们总是一样的)。

    我们将 Hash 函数值一样但原字符串不一样的现象称为哈希碰撞。

构造方法

通常我们采用的是多项式 Hash 的方法,对于一个长度为l的字符串s来说,我们可以这样定义多项式 Hash 函数:

f(s)=i=1ls[i]pli(modM)

例如,对于字符串xyz来说,其哈希函数的值为xp2+yp+z

在选定pM时,两者都最好选一个较大的质数,可以降低哈希的冲突率。

在这里推荐p的取值为13113311313131

M一般取大质数,还可以将M的值设成264,并且将hash数组的值类型设置成ULL。利用ULL的取值范围正好在[0,2641],用值的自然溢出代替取模。

字符串子串的哈希值获取

通过计算该字符串的所有前缀的哈希值,可以获得该字符串对应的所有子串的哈希值。

如对于字符串“ABCD”,要单独处理“A”、“AB”、“ABC”、“ABCD”四个前缀的哈希值。
10

1)把该字符串看作是一个P进制的数

2)把该字符串所对应的P进制数转换为十进制数,再mod M

经过以上两步操作,可以将一个任意的字符串转换成(0,M1)区间的一个数。

注意:

1)不能将字母映射成0。

2)该情况假定不存在冲突

常用实现方式:双值哈希

用两个mod值来验证是否是同一个字符串,从而减小其哈希冲突的概率

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

struct mo{
    int mo1;
    int mo2;
}m[100005];

bool cmp(mo a, mo b){
    if(a.mo1 != b.mo1)      return a.mo1 < b.mo1;
    else return a.mo2 < b.mo2;
}

int main()
{
    int n, ans = 1;
    string s;
    ll hash_val[100005];
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> s;
        for(int j = 0; j < s.length(); j++)		hash_val[i] = (hash_val[i] * 62) + s[j];
        //判断冲突
        m[i].mo1 = hash_val[i] % ll(1e9 + 7);
        m[i].mo2 = hash_val[i] % ll(1e9 + 9);
    }
    sort(m + 1, m + n + 1, cmp);
    for(int i = 1; i < n; i++){
        if(m[i].mo1 == m[i+1].mo1 && m[i].mo2 == m[i+1].mo2)    continue;
        ans ++;
    }
    cout << ans << endl;
    return 0;
}

本文作者:hsy2093

本文链接:https://www.cnblogs.com/hsy2093/p/18610052

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hsy2093  阅读(74)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起