Hash

简要说明

  • \(OI\)中一般采用进制\(hash\).模数可以用\(unsigned \ long \ long\)自然溢出,也可以使用大质数.值得一提的是,\(unsigned\ long\ long\)的优点是好写,不用取模,缺点是可能会被良心出题人卡.如果为了万无一失,可以写双模数\(hash\),这个东西在绝大多数情况下可以保证正确性(参见Hash Killer III).
  • 关于进制\(hash\)两个最基础的东西:

\(Hash[0]=0.Hash[i]=Hash[i]*Base+s[i]\ for\ i>0.\)

\(hash(l,r)=Hash[r]-Hash[l-1]*Base^{r-l+1}.\)

  • 如果两个字符串按照如上算法得出的\(hash\)值相等,我们认为这两个字符串是相同的.
  • 下面是最近整理的关于\(hash\)的一些基础题.

题目

子串查找

  • 题意:给两个字符串\(a,b\),询问\(b\)在\(a\)的几个位置出现过.
  • 此题用\(hash\)做的话,只需求出字符串\(b\)的\(hash\)值,在字符串\(a\)中\(O(n)\)枚举每个长度等于\(|b|\)的子串,判断\(hash\)值是否相等即可.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jp

图书管理

  • 题意:你需要维护一个字符串集合,支持插入字符串和询问一个字符串是否在其中.
  • 将每个字符串通过计算\(hash\)转化为整数,开一个桶记录是否出现过即可.
View code

#include
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jp

Power Strings

  • 题意:求一个字符串的最短循环节长度.
  • 暴力枚举循环节长度,对于每个可以整除总长度的循环节长度,依次算出每段的\(hash\)值,若全部相等,说明这是一个合法的循环节.
  • 这样枚举的时间复杂度为\(O(n\cdot lnn)\),因为这是一个调和级数,函数\(lnx\)与它是共犯.
  • 此题还可用\(fail(next)\)数组计算.最小循环节长度为\(\frac{n}{n-fail[n]}\),若其为整数.否则为\(|S|\).
  • 后面还有一个相似的题,所以此题并没有代码.

Seek the Name, Seek the Fame

  • 题意:找出一个字符串中既是前缀又是后缀的子串的所有长度.
  • 此题使用\(hash\)十分简单,直接枚举每个前缀,与长度相等的后缀比较即可.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jp=1;--i)
printf("%d ",ans[i]);
puts("");
}
return 0;
}


三个朋友

  • 现在有一个字符串\(U\),是由\(S\)复制一次自身拼接后,再插入一个字符得到的.给出\(U\),若\(S\)唯一存在,求出\(S\),否则输出不存在或有多个.
  • 做法十分暴力.直接枚举是插入的哪一个字符.然后通过进制\(hash\)良好的运算能力求出两侧的\(hash\)值比较即可.注意细节的实现.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jpr)
return 0;
return (Hash[r]-Hash[l-1]*Pow[r-l+1]);
}
inline ull mergehash(ull hash1,ull hash2,int len)
{
return hash1*Pow[len]+hash2;
}
int main()
{
n=read();
scanf("%s",s+1);
if(n%2==0)
{
puts("NOT POSSIBLE");
return 0;
}
Pow[0]=1;
for(int i=1;in/2)
{
for(int i=1;i

A Horrible Poem

  • 题意:给定一个字符串\(S\),询问若干次,每次询问\(S\)某个子串的最短循环节.
  • 此题和\(Power\ Strings\)类似,但要询问子串的答案多次.
  • 考虑\(kmp\)的做法,计算\(fail\)其实只需检验最长前后缀公共长度.
  • 用\(hash\)判断,每次枚举这个子串长度的质因数进行检验.若\(d\)不是循环节长度,显然\(kd(k\in \mathbb{N_+})\)也不可能是循环节长度.可以作为一个小优化.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jpMAXN)
break;
ism[prime[j]*i]=1;
mindiv[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
}
}
}
inline ull calchash(int l,int r)
{
return Hash[r]-Hash[l-1]*Pow[r-l+1];
}
inline int judge(int l,int r,int len)
{
return (calchash(l,l+len-1)==calchash(r+1-len,r));
}
int main()
{
n=read();
scanf("%s",s+1);
Pow[0]=1;
Hash[0]=0;
for(int i=1; i

Beads

  • 题意:求一个串的最大循环节数目,这里规定最后一段不足循环节长度的段可以舍弃,并且段可以反转,即\(123\)和\(321\)被视作等价.
  • 显然只需要正着反着处理两个\(hash\)值,由于可以舍去最后一段,所以我们不使用计算\(fail\)的办法,而是\(O(n\cdot lnn)\)暴力枚举即可.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jp Hash;
ull prehash[MAXN],sufhash[MAXN];
ull Pow[MAXN];
int ans[MAXN],tp=0;
int res[MAXN];
int n,maxx=0;
int s[MAXN];
int main()
{
n=read();
Pow[0]=1;
for(int i=1;imaxx)
maxx=res[i],tp=0;
if(res[i]==maxx)
ans[++tp]=i;
}
printf("%d %d\n",maxx,tp);
for(int i=1;i

Antisymmetry

  • 题意:定义反对称串是指满足取反后再前后翻转,恰好与原串相同的\(01\)串.现在给定一个\(01\)串,求出它的子串中反对称串的个数.
  • 不难发现,一个反对称串长度一定为偶数,且后半段取反后形成回文串.
  • 枚举每个位置作为对称中心,只用二分找出最大的半径长度.每次去掉两端的数后仍为反对称串,这个对称中心形成的共有半径长度个反对称串.
  • 检验时比较前半段\(hash\)值和后半段取反且逆向维护的\(hash\)值即可.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jp>1;
if(calchash(x-mid+1,x,0)==calchash(x+1,x+mid,1))
res=mid,l=mid+1;
else
r=mid-1;
}
return 1LL*res;
}
int main()
{
n=read();
scanf("%s",s+1);
Pow[0]=1;
for(int i=1;i=1;--i)
revHash[i]=revHash[i+1]*Base+(('1'-s[i])^1);
for(int i=1;i

门票

  • 题意:给定一个一阶线性递推关系的数列,求出前\(2e6\)项中是否出现了循环.若有,输出开始循环的位置.数列中元素\(\leq 1e9\).内存限制\(4M\).
  • 比较直接的想法是开\(map\)水过去,但大概率会\(TLE\),且超过了空间限制.
  • 做法是\(hash\),将出现过的元素模上大质数后记录.但这样直接压缩,显然极容易造成\(hash\)冲突.一个简单的办法是模两个大质数,双\(hash\)即可解决问题.
  • 另一个做法是离线,将前\(2e6\)个数全部算出来,离散化后处理.这样做每次都是严格\(O(2e6\cdot log(2e6))\)的,可能会被卡常.但优点是有绝对严谨的正确性保证.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jp=0);
if(vis[res%P1][0]!=0 && vis[res%P1][0]==vis[res%P2][1])
{
printf("%d\n",i);
return;
}
if(!vis[res%P1][0])
vis[res%P1][0]=i;
if(!vis[res%P2][1])
vis[res%P2][1]=i;
}
puts("-1");
}
int main()
{
a=read(),b=read(),c=read();
A=a,B=b,C=c;
solve();
return 0;
}


收集雪花

  • 题意:给\(n\)个数,求区间内数字都不重复的最大区间长度.元素大小\(\leq 1e9\),\(n \leq 1e6\).
  • 数据结构学多了使人变傻.
  • 首先需要离散化或\(hash\),将值域控制在\(1e6\)的级别.
  • 然后,若直接上主席树,每次二分答案,枚举左端点,是\(O(nlog^2n)\)的,十分弱智.
  • 优秀的做法是\(Two\ Pointers.\)维护两个指针\(l,r\),不断往后跳,同时用类似于莫队的方法维护当前区间内颜色种类数目.合法时更新答案.
  • 这样每个指针最多跳\(n\)次,一共跳\(2n\)次,相当优秀.
View code

#include"bits/stdc++.h"
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp='0'&&jp

## 小结
- 字符串 $Hash$ 在 $OI$ 中十分优秀,尤其是进制 $Hash$ .通过 $Hash$ ,我们可以将字符串比较是否相等的 $O(n)$ 降至 $O(1)$ ,这样以来,许多十分暴力的思想/做法也可以通过.
- 离散化也可以算作是 $Hash$ 的一种,不过在转化的同时还要保证大小关系.

Hash学习小结的更多相关文章

  1. 插头$DP$学习小结

    插头\(DP\)学习小结 这种辣鸡毒瘤东西也能叫算法... 很优秀的一个算法. 最基本的适用范围主要是数据范围极小的网格图路径计数问题. 如果是像\(Noi2018\)那种的话建议考生在其他两道题难度 ...

  2. flex学习小结

    接触到flex一个多月了,今天做一个学习小结.如果有知识错误或者意见不同的地方.欢迎交流指教. 画外音:先说一下,我是怎么接触到flex布局的.对于正在学习的童鞋们,我建议大家没事可以逛逛网站,看看人 ...

  3. Python 学习小结

    python 学习小结 python 简明教程 1.python 文件 #!/etc/bin/python #coding=utf-8 2.main()函数 if __name__ == '__mai ...

  4. react学习小结(生命周期- 实例化时期 - 存在期- 销毁时期)

    react学习小结   本文是我学习react的阶段性小结,如果看官你是react资深玩家,那么还请就此打住移步他处,如果你想给一些建议和指导,那么还请轻拍~ 目前团队内对react的使用非常普遍,之 ...

  5. objective-c基础教程——学习小结

    objective-c基础教程——学习小结   提纲: 简介 与C语言相比要注意的地方 objective-c高级特性 开发工具介绍(cocoa 工具包的功能,框架,源文件组织:XCode使用介绍) ...

  6. pthread多线程编程的学习小结

    pthread多线程编程的学习小结  pthread 同步3种方法: 1 mutex 2 条件变量 3 读写锁:支持多个线程同时读,或者一个线程写     程序员必上的开发者服务平台 —— DevSt ...

  7. ExtJs学习笔记之学习小结LoginDemo

    ExtJs学习小结LoginDemo 1.示例:(登录界面) <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  8. 点滴的积累---J2SE学习小结

    点滴的积累---J2SE学习小结 什么是J2SE J2SE就是Java2的标准版,主要用于桌面应用软件的编程:包括那些构成Java语言核心的类.比方:数据库连接.接口定义.输入/输出.网络编程. 学习 ...

  9. (转) Parameter estimation for text analysis 暨LDA学习小结

    Reading Note : Parameter estimation for text analysis 暨LDA学习小结 原文:http://www.xperseverance.net/blogs ...

随机推荐

  1. [小问题笔记(四)] Enum枚举类型转换为DataTable( C# )

    枚举: public enum ProductType { 小产品=, 大产品, 超大产品 } 转换方法: /// <summary> /// 枚举类型转化为DataTable /// & ...

  2. filezilla无法启动传输及严重文件传输错误

    filezilla无法启动传输 严重文件传输错误 文件夹权限不够,修改之. 你的空间或服务器已经满了,请空下回收站或者扩容. 文件正在被占用,关闭后传输 ​

  3. NoSQL&&Redis介绍

    再说Redis之前,想先说一下NoSQL.在最早的单机时代,随着数据的增加一台机器可能放不下了.同时索引占用的内存空间也会越来越大.对请求的读写操作影响很大.于是就在数据库之前增加了一层保护层 — 缓 ...

  4. 【Bitset】重识

    ---------------------------------------------------------------------------- 一题题目: 一题题解: 这个题目哪来入门再好不 ...

  5. jQuery中兄弟元素、子元素和父元素的获取

    我们这里主要总结jQuery中对某元素的兄弟元素.子元素和父元素的获取,原声的Javascript代码对这些元素的获取比较麻烦一些,而jQuery正好对这些方法进行封装,让我们更加方便的对这些元素进行 ...

  6. SSM 框架搭建 idea环境

    参考: https://www.cnblogs.com/toutou/p/ssm_springmvc.html https://www.cnblogs.com/toutou/p/ssm_springm ...

  7. JSP 标准标签库(JSTL)

    JSP 标准标签库(JSTL) JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能. JSTL支持通用的.结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签, ...

  8. 重新学习MySQL数据库1:无废话MySQL入门

    重新学习Mysql数据库1:无废话MySQL入门 开始使用 我下面所有的SQL语句是基于MySQL 5.6+运行. MySQL 为关系型数据库(Relational Database Manageme ...

  9. mac下通过brew切换php版本

    第一步,先安装 brew    Brew 是 Mac 下面的包管理工具,通过 Github 托管适合 Mac 的编译配置以及 Patch,可以方便的安装开发工具. Mac 自带ruby 所以安装起来很 ...

  10. mac下csv乱码解决办法

    到csv目录下, 用终端执行以下命令: iconv -f UTF8 -t GB18030 a.csv >b.csv