字符串哈希详解,单hash,双hash,滚动哈希

news2024/9/21 14:29:20

一、字符串哈希

1.1 基本概念

字符串哈希不同的字符串映射成不同的整数。

思想:将字符串映射成一个 p进制数字

我们定义如下哈希函数
h a s h ( s ) = ∑ i = 1 n s [ i ] × p n − i ( m o d   M ) 其中 s 为长度为 n 的字符串,下标从 1 开始 \begin{align} & hash(s) = \sum_{i=1}^{n}s[i]\times p^{n - i} (mod \ M) \\ & 其中s为长度为n的字符串,下标从1开始 \end{align} hash(s)=i=1ns[i]×pni(mod M)其中s为长度为n的字符串,下标从1开始
例如:p = 131,s = abc,其哈希值为 97 × 131 2 + 98 × 131 + 99 97 \times {131}^2 + 98 \times 131 + 99 97×1312+98×131+99

显然,有时会存在多个不同的字符串哈希值相同的情况,我们通常的处理策略是巧妙设置p和M的值,往往取p为某个质数,M为某个大质数。

关于 p 的选择:常见的有131、31、13331等。

关于 M,由于 M 我们要取一个比较大的质数,而出题人往往对一些比较经典的质数如1e9 + 7、998244353等构造一堆卡哈希的数据,所以我们往往通过捕获一个随机数,根据随机数往下再取质数,来尽可能避免被hack。

还有的处理方式如:双模数hash,甚至三模数hash,虽然有一定作用,但是运算多了之后,时间复杂度的常数自然增大

下面只介绍自然溢出法的单hash双hash以及随机模底hash,多了也没必要,字符串哈希往往是作为算法优化的某一步骤,如果双hash都能被卡,说明题目可以采取其它优化策略,如:AC自动机、SA等。

1.2 实现方式

1.2.1 单hash(自然溢出法)
constexpr int base = 131;
std::vector<size_t> h(n + 1);
for (int i = 0; i < n; ++ i) {
    h[i + 1] = h[i] * base + s[i];
}
1.2.2 双hash(自然溢出法)
constexpr int base = 131;
std::vector<size_t> h1(n + 1), h2(n + 1);
for (int i = 0; i < n; ++ i) {
    h1[i + 1] = h1[i] * base1 + text[i];
    h2[i + 1] = h2[i] * base2 + text[i];
}
1.2.3 随机模底hash

随机模底哈希就是用随机数来生成base,同时抛弃自然溢出法,采用对一个大质数取模。

由于往往用时间戳生成随机数,所以被hack的几率也较小。但是,字符串哈希始终是有风险的。

std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
const int P = findPrime(rng() % 900'000'000 + 900'000'000), 
base = uniform_int_distribution<>(8e8, 9e8)(rng);
std::vector<int> h(n + 1), p(n + 1);
p[0] = 1;
for (int i = 1; i <= n; ++ i)
    p[i] = 1LL * p[i - 1] * base % P;
for (int i = 0; i < n; ++ i) {
    h[i + 1] = (1LL * h[i] * base % P + text[i]) % P;
}

1.3 滚动hash

滚动哈希解决的问题:对于一个字符串,我们如何O(1)获取任意子串的hash值?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如上图,我们要获取蓝色部分的hash值,我们用类似前缀和的方式可以获取:

h(s(5, 8)) = h(8) - h(4) * base ^ 4

这种获取子串hash值得方式我们就称为滚动哈希

1.4 OJ 练习

1.4.1 模板

P3370 【模板】字符串哈希 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <bits/stdc++.h>

using i64 = long long;

bool isprime (int x) {
    if (x <= 1) return false;
    for (int i = 2; i * i <= x; ++ i)
        if (x % i == 0) 
            return false;
    return true;
}

int findPrime (int x) {
    while (!isprime(x))
        ++ x;
    return x;
}


void solve() {
    std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());

    const int P = findPrime(rng() % 900'000'000 + 900'000'000), base = std::uniform_int_distribution<>(8e8, 9e8)(rng);

    int n;
    std::cin >> n;
    std::vector<int> a(n);

    for (int i = 0; i < n; i ++ ) {
        std::string s;
        std::cin >> s;
        int h = 0;
        for (char ch : s)
            h = (1LL * base * h + ch - '0') % P;
        a[i] = h;
    }

    std::sort(a.begin(), a.end());
    a.resize(std::unique(a.begin(), a.end()) - a.begin());
    
    std::cout << a.size();
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int _ = 1;
    // std::cin >> _;
    while (_ --)
        solve();
    return 0;
}

::cout.tie(nullptr);
int _ = 1;
// std::cin >> ;
while (
--)
solve();
return 0;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1921952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动驾驶中,实现三维点旋转原理

文章目录 1. 三维点旋转的方案2. 使用复数表示二维点的旋转2.1. 复数的概念2.2. 复数的三种形式及相互转换2.3. 复数概念扩展&#xff1a;实数、虚数、复数 3. 四元数旋转三维点原理4. 使用四元数进行旋转的公式5. 旋转叠加6. 四元数转换为三维点7. 代码实现 1. 三维点旋转的方…

《昇思25天学习打卡营第19天|生成式-Pix2Pix实现图像转换》

学习内容&#xff1a;Pix2Pix实现图像转换 1.模型简介 Pix2Pix是基于条件生成对抗网络&#xff08;cGAN, Condition Generative Adversarial Networks &#xff09;实现的一种深度学习图像转换模型&#xff0c;该模型是由Phillip Isola等作者在2017年CVPR上提出的&#xff0c…

基于javaScript的简易ATM机

目录 一.设计要求 二.设计思路 三.案例展示 四.源代码展示 一.设计要求 首先里面已经存放了100元钱。如果用户执行存钱操作&#xff0c;就将输入进去的存钱数目和现有的钱相加&#xff0c;再显示余额提示框。如果用户执行取钱操作&#xff0c;就用现有的钱减去要取出钱的数目…

AutoHotKey自动热键(七)WINDOWS按键映射与鼠标映射(替换/组合)

该脚本支持简单的按键替换,可以针对某个窗口进行按键替换,还可以对鼠标和键盘进行互相替换,也可以用来禁用一些按键 键盘按键映射 普通按键映射 a::b这样子就直接在全局把所有的a键输入都映射成b键输出 上面这一行在执行过程中相当于拆解成下面两个,第一个是按下,第二个是弹…

【刷题汇总 --游游的水果大礼包、买卖股票的最好时机(二)、倒置字符串】

C日常刷题积累 今日刷题汇总 - day0111、游游的水果大礼包1.1、题目1.2、思路1.3、程序实现 2、买卖股票的最好时机(二)2.1、题目2.2、思路2.3、程序实现 3、倒置字符串3.1、题目3.2、思路3.3、程序实现 -- c语言3.4、程序实现 -- c 4、题目链接 今日刷题汇总 - day011 1、游游…

U盘打不开的终极解决方案:原因剖析、恢复策略与预防之道

U盘困境&#xff1a;打不开的焦虑与应对 在数字化时代&#xff0c;U盘作为数据交换与存储的重要工具&#xff0c;几乎成为了每个人工作、学习和生活中的必需品。然而&#xff0c;当您满怀期待地将U盘插入电脑&#xff0c;却遭遇“无法识别”、“无法访问”等提示&#xff0c;U…

安装isce2

今天再次尝试安装&#xff0c;之前试过2次都是卡在同一步&#xff0c;今天换成了用mamba conda就没有再报错了 全程参考云军老师的step by step教程&#xff0c;安装成功 GitHub - yunjunz/conda-envs: conda environment setup on Linux / macOS for InSAR data processing …

初识Spring Web MVC

1. 什么是 Spring Web MVC&#xff1f; Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架&#xff0c;从⼀开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc)&#xff0c;但它通常被称为"SpringMVC".Servlet&am…

宝马退出价格战,19万买不到i3了

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 宝马退出价格战 这一消息&#xff0c;源自知名汽车博主孙少军。 7月11日他发文称&#xff0c;“因价格战导致门店亏损严重&#xff0c;宝马7月将会开始降量保价。” 第二天他又做了补充&#xff0c…

SQL,python,knime将数据混合的文字数字拆出来,合并计算实战

将下面将数据混合的文字数字拆出来&#xff0c;合并计算 一、SQL解决&#xff1a; ---创建表插入数据 CREATE TABLE original_data (id INT AUTO_INCREMENT PRIMARY KEY,city VARCHAR(255),value DECIMAL(10, 2) );INSERT INTO original_data (city, value) VALUES (上海0.5…

如何判断代码是否是在UPDATE TASK的Session中执行?

1. 背景 有时我们想控制ABAP代码在UPDATE TASK中的逻辑&#xff0c;例如某些逻辑执行&#xff0c;某些逻辑不执行。 那么&#xff0c;我们应该如何判断当前代码运行的环境呢&#xff1f;也即&#xff0c;怎么知道一段运行时代码是运行在当前的ABAP session中&#xff0c;还是…

红帽官方福利:RHCE赠送免费补考

RHCE 红帽官方活动【免费补考来了】 新班 7月27日 RHCE 周末班 学完9月底考试&#xff0c;享受免费补考&#xff01; RHCE9.0 &#xff08;点击查看课程介绍&#xff09; 上课课时&#xff1a;72课时/12天考试辅导1-2天 开班频率&#xff1a;2个月开一期 最新新班&#x…

高精度定位与AI技术的深度融合——未来智慧世界的钥匙

引言在当今迅速发展的科技时代&#xff0c;精确定位和人工智能&#xff08;AI&#xff09;技术正在快速推动各领域的创新与变革。高精度定位结合AI技术所产生的融合效应&#xff0c;正在加速智慧城市、智能驾驶、智能物流以及许多其他领域的实现。这篇文章将详细探讨高精度定位…

栈(Stack)与队列(Queue,Deque)

前言&#xff1a; 栈与队列在数据结构中用法都相对比较简单&#xff0c;是数据结构中经常用到的两种。 1.栈&#xff08;Stack&#xff09; &#xff08;1&#xff09;特点&#xff1a; 先入后出&#xff0c;后入先出。栈的底层就是一个数组&#xff08;java原生库中&#x…

uniapp发送Form Data格式请求

设置header的Content-Type为 application/x-www-form-urlencoded 即可 uni.request({url: , // 接口urldata: {input: 写一篇一千字的作文}, // 入参method: POST, // 参数类型header: {"Content-Type": "application/x-www-form-urlencoded"}, // 请求头…

本地部署,isNet 图像背景去除

目录 摘要 引言 ISNet 架构 关键组件 技术原理 本地部署 运行结果 结论 参考文献 GitHub - xuebinqin/DIS: This is the repo for our new project Highly Accurate Dichotomous Image SegmentationThis is the repo for our new project Highly Accurate Dichotomous…

介绍一款数据准实时复制(CDC)中间件 `Debezium`

简介 文章开头先介绍一下什么是CDC。数据准实时复制(CDC)是目前行内实时数据需求大量使用的技术。常用的中间件有Canal、Debezium、Flink CDC等 下面我们做一下对比 各有优缺点吧,本主要介绍一下Debezium中间件。 Debezium是什么 Debezium是一个为变更数据捕获(CDC)提供…

220.贪心算法:根据身高重建队列(力扣)

代码解决 class Solution { public:// 定义排序规则&#xff1a;首先按身高降序排序&#xff0c;如果身高相同则按k值升序排序static bool cmp(const vector<int>&a, const vector<int>&b){if (a[0] b[0]) return a[1] < b[1]; // 如果身高相同&#…

注册自定义总线

1、在/sys/bus下注册一个自定义总线 #include<linux/module.h> #include<linux/init.h> #include<linux/kernel.h> #include<linux/kobject.h> #include<linux/slab.h> #include<linux/sysfs.h> #include<linux/device.h> #include…

【Linux】1w详解如何实现一个简单的shell

目录 实现思路 1. 交互 获取命令行 2. 子串分割 解析命令行 3. 指令的判断 内建命令 4. 普通命令的执行 补充&#xff1a;vim 文本替换 整体代码 重点思考 1.getenv和putenv是什么意思 2.代码extern char **environ; 3.内建命令是什么 4.lastcode WEXITSTATUS(sta…