字符串哈希

一. 概念

一.什么是字符串哈希

hash,其实就是将一个东西映射成另一个东西,类似Map,key对应value。

那么字符串Hash,其实就是:构造一个数字使之唯一代表一个字符串。但是为了将映射关系进行一一对应,也就是,一个字符串对应一个数字,那么一个数字也对应一个字符串。

用字符串Hash的目的是,我们如果要比较一个字符串,我们不直接比较字符串,而是比较它对应映射的数字,这样子就知道两个“子串”是否相等。从而达到,子串的Hash值的时间为 O(1),进而可以利用“空间换时间”来节省时间复杂的。

二.什么是哈希函数

哈希函数是哈希的关键,首先理论上任何一个函数都能做哈希函数,但是在字符串哈希中,为了避免冲突采用了一种进制哈希的方式(BKDRHash)。

字符串哈希【算法】_字符串哈希算法-CSDN博客  (原文摘自)
原理:设定一个进制 P,需要计算一个字符串的哈希值时,把每个字符看成每个进制位上的一个数字,这个串转化成了一个基于进制 P 的数,最后对 M 取余数,就得到了这个字符串的哈希值。为简化计算可以取空间大小为 M=264是 unsigned long long 的长度,一个 unsigned long long 型的哈希值 H,当 H 值大于 M 时会自动溢出,等价于自动对 M 取余,这样能避免低效的取余运算。

 这里我们给出一个构造哈希函数的模版

ull hash(string s){
	ull res=0;
	for (int i=0;s[i];i++){
		res=(res*base1+(ull)s[i])%mod1;
	}
	return res;

 

 


 二.例题分析

一.P3370 【模板】字符串哈希

https://www.luogu.com.cn/problem/P3370 

如题,给定 N 个字符串(第 i 个字符串长度为 Mi​,字符串内包含数字、大小写字母,大小写敏感),请求出 N 个字符串中共有多少个不同的字符串。

友情提醒:如果真的想好好练习哈希的话,请自觉。

输入格式

第一行包含一个整数 N,为字符串的个数。

接下来 N 行每行包含一个字符串,为所提供的字符串。

输出格式

输出包含一行,包含一个整数,为不同的字符串个数。

输入输出样例

输入 #1复制

5
abc
aaaa
abc
abcc
12345

输出 #1复制

4

说明/提示

对于 30% 的数据:N≤10,Mi​≈6,Mmax≤15。

对于 70% 的数据:N≤1000,Mi​≈100,Mmax≤150。

对于 100% 的数据:N≤10000,Mi​≈1000,Mmax≤1500。

样例说明:

样例中第一个字符串(abc)和第三个字符串(abc)是一样的,所以所提供字符串的集合为{aaaa,abc,abcc,12345},故共计4个不同的字符串。

 

 一道字符串哈希的模版题,这里可以更这代码了解一下字符串哈希的题目的解题过程

单哈希(自然溢出)

#include<bits/stdc++.h>
using namespace std;
#define N 1000005
typedef unsigned long long ull;     //重命名,ull代表unsigned long long
ull base=131;            //映射的进制
ull f[N];                //存储字符串映射后的数字
char s[N];         //存储原字符串
int n,ans=1;

ull haxi(char s[])     //hash函数
{
    int len=strlen(s);
    ull sum=0;
    for(int i=0;i<len;i++){
        sum=sum*base+(ull)s[i];      //具体的映射过程
    }
    return sum;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        f[i]=haxi(s);      //将映射后的值赋值给f数组
    }
    sort(f+1,f+n+1);       //升序排列,方便后面比对有多少重复的
    for(int i=1;i<n;i++){
        if(f[i]!=f[i+1])   //如果不是重复相同的字符串,结果加1
            ans++;
    }
    cout<<ans<<endl;
    return 0;
}

 以上是通过单哈希的方式,其实我们在做题中可能会遇到不同的字符串却对应的相同的值,这时就会发生哈希冲突,为了减少哈希冲突,就可以通过双哈希的方式解决。

三.双Hash方法


用字符串Hash,最怕的就是,出现冲突的情况,即不同字符串却有着相同的hash值,这是我们不想看到的。所以为了降低冲突的概率,可以用双Hash方法。

将一个字符串用不同的Base和MOD,hash两次,将这两个结果用一个二元组表示,作为一个总的Hash结果。

相当于我们用不同的Base和MOD,进行两次 单Hash方法 操作,然后将得到的结果,变成一个二元组结果,这样子,我们要看一个字符串,就要同时对比两个 Hash 值,这样子出现冲突的概率就很低了。

那么对应的 Hash 公式为:

 

#include<bits/stdc++.h>     //双哈希操作和单哈希操作其实是一致的,只是多一次哈希
using namespace std;
typedef unsigned long long ull;
ull base=131;
struct data
{
    ull x,y;
}a[10010];       //结构体存储两个哈希值
char s[10010];
int n,ans=1;
ull mod1=19260817;  //mod值改变
ull mod2=19660813;
ull hash1(char s[])     //两次哈希操作
{
    int len=strlen(s);
    ull ans=0;
    for (int i=0;i<len;i++)
        ans=(ans*base+(ull)s[i])%mod1;
    return ans;
}
ull hash2(char s[])
{
    int len=strlen(s);
    ull ans=0;
    for (int i=0;i<len;i++)
        ans=(ans*base+(ull)s[i])%mod2;
    return ans;
}
bool comp(data a,data b)    //结构体排序内部比较函数
{
    return a.x<b.x;
}
main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s);
        a[i].x=hash1(s);
        a[i].y=hash2(s);
    }
    sort(a+1,a+n+1,comp);  //结构体排序,升序
    for (int i=2;i<=n;i++)
        if (a[i].x!=a[i-1].x || a[i-1].y!=a[i].y)  //两次比较
            ans++;
    printf("%d\n",ans);
}

 

 如果还对字符串哈希不明白的可以在b站上晓董老师的视频讲解(链接已附上)。

字符串哈希算法是一种将字符串映射为数字的算法,常用于字符串的比较和匹配。在C++中,可以使用字符串哈希算法来加速字符串的比较操作。 引用\[1\]中的代码示例展示了一个使用字符串哈希算法的C++代码。该代码使用了前缀和数组和字符串数组来存储字符串,并通过计算哈希值来比较两个子串是否相等。其中,哈希值的计算使用了前缀和数组和幂运算。 引用\[2\]中的解释指出,使用字符串哈希的目的是为了比较字符串时不直接比较字符串本身,而是比较它们对应映射的数字。这样可以将子串的哈希值的时间复杂度降低到O(1),从而节省时间。 引用\[3\]中的代码示例也展示了一个使用字符串哈希算法的C++代码。该代码使用了前缀和数组和字符串数组来存储字符串,并通过计算哈希值来比较两个子串是否相等。与引用\[1\]中的代码类似,哈希值的计算也使用了前缀和数组和幂运算。 综上所述,字符串哈希算法是一种将字符串映射为数字的算法,常用于字符串的比较和匹配。在C++中,可以使用前缀和数组和幂运算来计算字符串哈希值,并通过比较哈希值来判断两个子串是否相等。 #### 引用[.reference_title] - *1* [C++算法题 # 33 字符串哈希](https://blog.csdn.net/weixin_44536804/article/details/123425533)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [字符串哈希(c++)](https://blog.csdn.net/qq_41829492/article/details/120980055)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [AcWing 841. 字符串哈希(C++算法)](https://blog.csdn.net/YSA__/article/details/108453403)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值