首页 > 其他分享 >字符串哈希笔记

字符串哈希笔记

时间:2023-03-21 21:12:21浏览次数:39  
标签:Hash int long 笔记 哈希 字符串 const

目录

字符串哈希

1. 定义

一个把字符串映射到整数的函数\(f\),这个\(f\)被称为哈希函数

这个函数的作用:希望可以判断两个字符是否相等;

1.1 Hash的思想

核心思想在于:

如何将一个字符串映射到一个值域较小、方便比较的范围

大范围映射到小范围:

对一个大数进行取模,例如一个大的质数

注意:

  1. 在字符串哈希中,值域需要小到能够快速比较, 例如\(10^ {18}\) 或者 \(10^ 9\) 都是可以快速比较的)

哈希性质:

  1. 若Hash值不一样,那么两个字符串一定不相等;
  2. 若Hash值一样,那两个字符串不一定相等,当时大概率一样;

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

1.2 Hash的计算和改进

哈希需要关注的有时间复杂度和Hash的准确率

对于一个长度为L的字符串s来说,可以这样定义Hash的表达式如下:

\[f(s) ={ \sum \limits_{i=1}^L {s[i] * b^{L - i}} }\ (mod\ {M}) \]

即对于字符串s = "xyz"来说,如下计算

x y z

其中哈希值计算公式为: \(f(s) = xb^2 + yb + z\),可以理解为一个P进制的计算;

经验选择:

  1. \(M = 2^{64}\), 即 unsigned long long 类型的最大取值范围;
  2. \(b = 131\)或者 \(b = 13131\)

1.3 自己的常用实现

具体例子: 字符串S为\(X_1X_2X_3...X_n\),把字符串变成一个P进制数字,具体方法如下:

\[(X_1P^{n-1} + X_2P^{n-2} + ... + X_iP^{n - i} + ... + X_nP^0) \ mod \ Q \]

为了方便计算P[N],可以提前计算好每个值,如下:

typedef unsigned long long ULL;
const int N = 1E5+7;
const int p = 131;

ULL P[N], s[N];

void init() {
    P[1] = 1;
    
    for (int i = 2; i <= N; i++)
        P[i] = P[i-1] * p;
}

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

\[h(i+1) = h(i) * P + s[i], \ i \ \epsilon \ [0,n-1] \]

区间和公式:

\[h[l,r] = h[r] - h[l - 1]P^{r - l + 1} \]

字符串中求子串的哈希的区间和公式的理解:

ABCDEFABC的前三个字符串一样,那么子串DEF的哈希值计算可以理解为ABC的哈希值 * P^2变成ABC000,则俩式做减法即可得DEF的哈希值

代码的简单实现如下:

unsigned long long getHash(int l ,int r) {
    return h[r] - h[l-1] * P[r-l+1];
}

2. 代码实现

2.1 暴力版本:

#include <iostream>
#include <cstring>

typedef unsigned long long ULL;

const int N = 1E5+7;//  字符串最大长度
const int b = 131;
const int M = 1E9+7;

ULL getHash(const string &s) {
    ULL res = 0;
    for (int i = 0; i < s.size(); i++) {
        res = (s[i] +  res * b) % M; // 这里如果越界了,即等于 Mod 2^64
    }
    
    return res;
}

2.2 字符串前缀和哈希


typedef unsigned long long ULL;
const int N = 1E5+7;
const int p = 131;

ULL P[N], s[N];

void init(int n) {
    P[0] = 1;
    
    for (int i = 0; i < n; i++)
        P[i+1] = P[i] * p;
        h[i+1] = h[i] * p + s[i];
}

unsigned long long getHash(int l ,int r) {
    return h[r] - h[l-1] * P[r-l+1];
}

参考文档

1. 字符串哈希

标签:Hash,int,long,笔记,哈希,字符串,const
From: https://www.cnblogs.com/zuixime0515/p/17241450.html

相关文章

  • [学习笔记] CDQ分治
    引入-分治分治,就是将讲原问题不断细分直到规模小到能够解决,然后一层层向上合并得到答案的过程。归并排序大致思想:把序列拆成左右两部分,分别归并排序,然后使用两个指针......
  • 学习记录:day03笔记
    一、数据类型为什么要对数据进行分类?1、现实中的数据就是自带类别属性的2、对数据进行分类可以节约内存存储空间、提高运行速度存储空间的单位:Bit比特存储1个......
  • 学习记录:day04笔记
    一、for循环语句循环:就是一种让代码反复执行的方式,从而达到想要的效果for循环一般会使用一个变量来引导循环的进行,这一变量叫做该循环的循环变量iindexfor循环的变......
  • 学习记录:day05笔记
    一、数组什么是数组:变量的组合,是一种批量定义相同类型变量的方式定义:类型名数组名[数量];intarr[5];注意:数组的内存空间是连续分配的,且数组的长度一旦确定就无......
  • 构建之法阅读笔记1
    一、我过去是怎么做的  过去,刚开始学C时,我还不知道这些编程语言能干什么用,而且老师也只是只讲课本知识,动手实践很少,导致现在回想大一时并没有什么收获可以回味。加上......
  • 学习记录:day06笔记
    一、Window下获取方向键1、导入头文件#include<conio.h>2、通过getch()获取键盘上的键值上:72下:80左:75右:77 二、Linux下获取方向键:1、在Window中把getch.h文......
  • 学习记录:day07笔记
    进制转换1、为什么使用二进制、八进制、十六进制?因为目前CPU只能识别高低两种电平,只能对二进制数据进行计算二进制虽然能够直接别计算机识别但是不方便人去书写和记......
  • [嵌入式RTOS]记录一下因浮点数转为字符串导致精度损失所踩的坑
    1.起因:工作中对接平台需要将设备的GPS数据传给平台,但是平台采用的不是回调函数将数据直接作为参数返回而是格式化的字符串命令,所以需要将double类型的gps数据格式化输出到......
  • react 官网学习笔记
    1.元素(html片段)和组件的关系(js函数)2.写组件的方式(function还是class)3.一个括号和两个括号的使用场景{}(获取值/js函数调用){{}}4.props和render都是做什......
  • 【笔记】electron + react + antd
    electronElectron是一个使用JavaScript、HTML和CSS构建桌面应用程序的框架。嵌入Chromium和Node.js到二进制的Electron允许您保持一个JavaScript代码代码库......