关于HASH

​ 这应该是经常使用的一个算法,因为其预处理后,优秀的\(O(1)\)处理出子串,并且\(O(1)\)比较,大快人心,而且写法简单,令人心情愉悦;

​ 但是其空间复杂度较高,并且有玄学模数以及哈希冲突,以至于如果想hack,其实可以hack掉;

前置知识

​ 关于进制,模数,hash就用到了重构进制,取模稀疏,所以哈希表又叫稀疏表;

入坑

​ hash很好理解,而且匹配非常方便,不容易写炸,对于萌新十分友好,

HASH查询

​ 我们知道数字匹配复杂度为 \(O(1)\) ,数字匹配速度快,而字符串却只能一个一个匹配, 这不公平 .那么我们考虑将字符串变成数字.

​ 想一下数字有进制,那么我们定义一下字母的进制,不一定是26,我们可以随便取一个数,习惯性取质数;

​ 但是数字太长,爆 \(long\) \(long\) 我们没办法存怎么办,我们考虑字符串很少,但是空间很大,我们考虑将数字安排入一个位置,其实这个位置是随机的,但是我们可以推出,这就够了;

​ 那么我们可以模一个数字,将数字限制在一个范围之内,然后储存下来,而这个模数一般是一个质数,因为质数的特殊性质,可以造成更好地将数字稀疏;

模版

#define ll long long
const ll base=133;
const ll mod=19491001;
ll id(char s[]){
int l=strlen(s);ll ol=0;
for(int i=0;i<l;i++)
ol=(ol*base+s[i]-'a'+1)%mod;
return ol;
}

​ 处理复杂度 \(O(n)\) 然后开个数组储存即可;

HASH子串匹配

​ 我们知道,如果每次都处理出一个串的子串,那么时间复杂度 \(O(n^2)\) ,这是我们不能接受的,但是考虑一下我们存的是数字,数字有进制,那么一定可以通过加减操作得到其子串,那么,就简单很多了;

​ 我们可以先预处理出 \(HASH\) 前缀和,之后通过加减得到一段区间的子串;

​ 但是直接开数组了话,有可能开不下,或者加大hash冲突的可能(即两个字符串hash值相同),那么我们可以考虑在不超时的情况下,加入一个map储存hash值,这样不需再担心空间问题,但是每次查询时间会多一个 \(logn\) ,请谨慎使用;

例题

​ 我不再直接附上模版,而是引入一个 例题;

给出几个由小写字母构成的单词,求它们最长的公共子串的长度。

做法

​ 我们可以二分枚举公共子串长度,因为公共子串长度一定是满足单调性质的.

​ 那么我们选择枚举第一个串的子串,然后将其他串中相同长度的子串储存起来,那么就可以匹配了;

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define maxn 2007
#define ll long long
using namespace std;
const ll base=133;
const ll mod=998244353;
map<int,bool>ha[6];
ll sum[6][maxn],ad[maxn];
int n,lim=maxn,len[6];
char c[6][maxn]; template<typename type_of_scan>
inline void scan(type_of_scan &x){
type_of_scan f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
template<typename top,typename... tops>
inline void scan(top &x,tops&... X){
scan(x),scan(X...);
} int id(int pos,int l,int r){
return (sum[pos][r+1]-sum[pos][l]*ad[r-l+1]%mod+mod)%mod;
} bool check(int l){
for(int i=1;i<=n;i++) ha[i].clear();
for(int i=2;i<=n;i++){
for(int j=0;j+l-1<len[i];j++)
ha[i][id(i,j,l+j-1)]=1;
}
for(int i=0;i+l-1<len[1];i++){
int temp=id(1,i,i+l-1),cnt=n-1;
for(int j=2;j<=n;j++){
if(ha[j][temp]) cnt--;
}
if(!cnt) return 1;
}
return 0;
} void init(){
ad[0]=1;
for(int i=1;i<=2001;i++) ad[i]=ad[i-1]*base%mod;
for(int i=1;i<=n;i++){
for(int j=0;j<len[i];j++){
sum[i][j+1]=(sum[i][j]*base+(c[i][j]-'a'))%mod;
}
}
} int main(){
scan(n);
for(int i=1;i<=n;i++)
scanf("%s",c[i]),lim=min(len[i]=strlen(c[i]),lim);
int l=0,r=lim;init();
while(l<r){
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
TO BE CONTINUED

hash应用的更多相关文章

  1. 复杂的 Hash 函数组合有意义吗?

    很久以前看到一篇文章,讲某个大网站储存用户口令时,会经过十分复杂的处理.怎么个复杂记不得了,大概就是先 Hash,结果加上一些特殊字符再 Hash,结果再加上些字符.再倒序.再怎么怎么的.再 Hash ...

  2. 对抗密码破解 —— Web 前端慢 Hash

    (更新:https://www.cnblogs.com/index-html/p/frontend_kdf.html ) 0x00 前言 天下武功,唯快不破.但在密码学中则不同.算法越快,越容易破. ...

  3. 散列表(hash table)——算法导论(13)

    1. 引言 许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表 在介绍散列 ...

  4. hash表长度优化证明

    hash表冲突的解决方法一般有两个方向: 一个是倾向于空间换时间,使用向量加链表可以最大程度的在节省空间的前提下解决冲突. 另外一个倾向于时间换空间,下面是关于这种思路的一种合适表长度的证明过程: 这 ...

  5. SQL Server-聚焦查询计划Stream Aggregate VS Hash Match Aggregate(二十)

    前言 之前系列中在查询计划中一直出现Stream Aggregate,当时也只是做了基本了解,对于查询计划中出现的操作,我们都需要去详细研究下,只有这样才能对查询计划执行的每一步操作都了如指掌,所以才 ...

  6. C# salt+hash 加密

    一.先明确几个基本概念 1.伪随机数:pseudo-random number generators ,简称为:PRNGs,是计算机利用一定的算法来产生的.伪随机数并不是假随机 数,这里的" ...

  7. SQL 提示介绍 hash/merge/concat union

    查询提示一直是个很有争议的东西,因为他影响了sql server 自己选择执行计划.很多人在问是否应该使用查询提示的时候一般会被告知慎用或不要使用...但是个人认为善用提示在不修改语句的条件下,是常用 ...

  8. 对一致性Hash算法,Java代码实现的深入研究

    一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...

  9. 巧用location.hash保存页面状态

    在我们的项目中,有大量ajax查询表单+结果列表的页面,由于查询结果是ajax返回的,当用户点击列表的某一项进入详情页之后,再点击浏览器回退按钮返回ajax查询页面,这时大家都知道查询页面的表单和结果 ...

  10. redis数据结构详解之Hash(四)

    序言 Hash数据结构累似c#中的dictionary,大家对数组应该比较了解,数组是通过索引快速定位到指定元素的,无论是访问数组的第一个元素还是最后一个元素,所耗费的时间都是一样的,但是数组中的索引 ...

随机推荐

  1. laravel邮件发送

    laravel邮件发送 使用邮件发送类Mail 文本 静态方法 raw() 富文本 静态方法 send() 注:使用邮件发送必须有邮件账号,需要开启smtp协议,现在主流服务器都支持,smtp默认端口 ...

  2. go mod 拉取私有仓库

    前言 如果代码中依赖了本地的包, 这个包是托管在内网 Gitlab 中, 而且不是 HTTPS 服务,那么应该怎样使用 go mod 拉取代码呢? 本文会给你我的答案 正文 首先我们要知道, 如果本地 ...

  3. maven依赖与传递性依赖

    目录 依赖范围 传递性依赖 依赖调节 可选依赖 本文主要是针对<maven实战>书中关键知识点的学习记录,未免有纰漏或描述不到之处,建议购买阅读原书 首先贴出一个pom常见的一些元素释义 ...

  4. Kubernetes 升级过程记录:从 1.17.0 升级至最新版 1.20.2

    本文记录的是将 kubernetes 集群从 1.17.0 升级至最新版 1.20.2 的实际操作步骤,由于 1.17.0 无法直接升级到 1.20.2,需要进行2次过滤升级,1.17.0 -> ...

  5. 05--Docker对DockerFile解析

    一.是什么: 1.1 DockerFile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本 1.2 构建步骤: 1.2.1 编写Dockerfile文件 1.2.2 docker ...

  6. 面试官问我CAS,我一点都不慌

    文章以纯面试的角度去讲解,所以有很多的细节是未铺垫的. 文章中写到的处理线程安全的思路每一项技术都可以写出一篇文章,AQS.Synchronized.Atomic...周末肝起来!下周再来给大家安排! ...

  7. .NET 项目中的单元测试

    .NET 项目中的单元测试 Intro "不会写单元测试的程序员不是合格的程序员,不写单元测试的程序员不是优秀的工程师." -- 一只想要成为一个优秀程序员的渣逼程序猿. 那么问题 ...

  8. 数据分析——Numpy/pandas

    NumPy NumPy是高性能科学计算和数据分析的基础包.部分功能如下: ndarray, 具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组. 用于对整组数据进行快速运算的标准数学函数(无需编 ...

  9. 将汉字取模软件中的汉字放到keil5中显示

    最近因为要使用STM32做毕业设计,需要用LCD显示中文,STM32开发板用的是原子的战舰STM32开发板,给的LCD显示例程里貌似没有中文显示,那么需要自己去编写中文显示程序. 软件编写对我来说并不 ...

  10. C#高级编程第11版 - 第二章 索引

    [1]2.1.1 Hello,World! 1. using static System.Console; // ... WriteLine("Hello World!"); 提前 ...