hash hash hash : ))

news/发布时间2024/5/12 23:39:00

hash:

hash简述,大概就是把一个字符串映射到一个整数上(这个整数就是一个自定义进制(mode)的数),通过比较该整数匹配字符串,这样可以实现字符串之间的O(1)匹配.
为什么要按位处理,因为这样方便分离字串.
怎么映射?就是将每位(i)分离乘上\(mode^{(i-1)}\),得到的映射整型就是这个字符串的hash值.
因为这个数一般无比巨大(在我们的字符串长度较高的情况下),所以我们需要一个模数来限制其大小.
一般情况下,我们的hash值都是unsigned long long(这样int×int不会炸),前面说要有一个模数限制,这样是不会炸整型了,但是可以看出其缺点,我们可能遇到hash值相同但是不匹配的串,我们称之为hash冲突.
那么我们再想想模数的选择,应该是一个较大的质数(这样减少hash冲突).
虽说较大质数可以减少hash冲突,但还是会有冲突,怎么办呢?取两个模数,而且要是不常用的模数(因为常用的容易被定向卡).
当两个模数下的hash值均相等,那么我们认为字符串匹配.
当然,我们在用unsinged long long 类型时,因为\(2^{64}-1\)就是个质数,所以不写模数让它自然溢出在一些不卡hash冲突的题目中还是很实用的(主要咱码量小啊).
另外一个实用的技巧就是分离字串hash值,得到hash值时类似于前缀和的操作决定了分离字串hash值的近O(1)的时间复杂度(虽然常数较大),这个是十分实用的.
我们只需预处理出所有位数应该对应乘上的mode的次数(p数组)以及某个串的任意一位到初始位的hash值(ha数组),任意一个字串\([i,j]\)的hash值就可以用\(ha[j]-ha[i-1]×(pow[j-i+1])\)得到.
例题(板子):乌力波

#include<bits/stdc++.h>
#define llu unsigned long long
#define lll long long
using namespace std;
const int N=1e6+20;
const int base=113;//进制数应该是一个质数
int n,h1,h2,ans;
char c1[N],c2[N];
llu p[N];//存储对应位乘数的大小 
llu ha[N];//存储查询的字符串的子串(从起点开始)hash值. 
llu cuthash(int l,int r)
{return ha[r]-p[r-l+1]*ha[l-1];
}
void init()
{cin>>n;p[0]=1;for(int i=1;i<=N-20;i++)p[i]=p[i-1]*base;while(n--){ans=0;cin>>(c1+1)>>(c2+1);int l1=strlen(c1+1),l2=strlen(c2+1);llu hash1=0;//存储判断的那个字符串的hash值.for(int i=1;i<=l1;++i)hash1=hash1*base+c1[i];for(int i=1;i<=l2;++i)ha[i]=ha[i-1]*base+c2[i];for(int i=1;i<=l2-l1+1;++i){llu k=cuthash(i,i+l1-1);//得到大小为l1字串的hash if(k==hash1)++ans;} cout<<ans<<'\n';}
}
int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);init();return 0;
}

另外就是一个双模数哈希的写法.
前文提及,自然溢出的hash明显不保险.
所以说就有了在自然溢出基础上去再加一个模数取hash值.
(当然,理论上来讲,只要这个模数别人不知道,就无法单向卡你的模数,所以貌似不带自然溢出的单模数hash也是可以的,可省时间).
那么简单叙述一下单模数hash的写法:首先要取一个较大的质数作为模数,其他的...好像没什么可讲的.
看代码吧.

llu ha[N],p[N];
//预处理每个位置上的乘数.
for(int i=1;i<=(1e5);++i)
{p[i]=p[i-1]*base%mod;
}
//取hash值,注意应该是long long类型,unsigned long long不行,不知道为什么有没有神犇给蒟蒻解释下...
ll gethash(int l,int r)
{return (ha[r]-ha[l-1]*p[r-l+1]%mod+mod)%mod;
}
//处理串s的所有从1到now的hash值
scanf("%s",s+1);
nowlen=strlen(s+1);
for(int now=1;now<=nowlen;++now)ha[now]=(ha[now-1]*base%mod+s[now])%mod;

就是这样了,这个模式大概不错.
至于有没有更简便的写法...本蒟蒻不知道...希望神犇指点.
例题链接及题解链接

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

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

Python多线程编程深度探索:从入门到实战

title: Python多线程编程深度探索:从入门到实战 date: 2024/4/28 18:57:17 updated: 2024/4/28 18:57:17 categories:后端开发tags:多线程 并发编程 线程安全 Python 异步IO 性能优化 实战项目第1章:Python基础知识与多线程概念 Python简介: Python是一种高级、通用、解释型…

U盘、硬盘泄密无处不在,如何锁紧企业数据大门?

在当今信息化的时代,数据泄露的问题尤为严重。特别是U盘、硬盘等移动储存设备,更是数据泄露的重灾区。那么,如何锁紧企业的数据大门呢?我们需要认识到信息安全就是一种生产要素,没有安全就没有生产。企业数据的安全性直接关系到企业的稳定和发展。也就是说,没有安全事故并…

asp.net core 多个授权策略选择单个策略

首先假设我们依据官方示例有这样一个自定义的授权handlerpublic class FunAuthorizeAttribute : AuthorizeAttribute, IAuthorizationRequirement,IAuthorizationRequirementData{public FunAuthorizeAttribute() : this(null, true) { }public FunAuthorizeAttribute(string f…

揭秘JavaScript数据世界:一文通晓基本类型和引用类型的精髓!

在编程的世界里,数据是构建一切的基础。就像建筑师需要了解不同材料的强度和特性一样,程序员也必须熟悉各种数据类型。 今天,我们就来深入探讨JavaScript中的数据类型,看看它们如何塑造我们的代码世界。 一、JavaScript数据类型简介 数据类型是计算机语言的基础知识,数据类…

如何将本地项目第一次同步到gitee远程

一,Gitee账号的注册/登录 在gitee登录入口输入相关信息进行注册登录https://gitee.com/signup#lang=zh-CN 二,本地安装git客户端并配置用户信息 1.Git - 安装 Git (git-scm.com)根据提示点击下一步,安装完成后,在本地文件夹右键单击出现git相关指令,表示安装成功2.点击git…

faiss简单测试方法

先把仓库克隆到本地,我这边还需要改cmake环境,在project上面加 set(CMAKE_CUDA_COMPILER /usr/local/cuda-11.8/bin/nvcc) 构建 mkdir buildcmake -B build . 编译,只需要编译faiss这部分就可以,(主目录下有很多测试代码,编译很慢,只编译faiss会快很多) cd build make …