字符串Hash小结

字符串Hash

什么是字符串Hash?

字符串Hash可以通俗的理解为,把一个字符串转换为一个整数。

如果我们通过某种方法(映射函数),将字符串转换为一个整数,这个映射函数不一定是单射函数,但是我们能尽量的选取一些参数(p和模数mod)来使得在该场景下尽量是一个单射函数。

如果有两个不同的字符串映射为一个同样的整数,这就产生了hash冲突

所以问题就是如何构造这个Hash函数,使得他们成为一个单射(不存在hash冲突) 。不用担心,接下来的内容正要讲解。

Hash函数

给定一个字符串 S = s 1 s 2 s 3.. s n S=s1s2s3..sn S=s1s2s3..sn,对字母 x x x,我们规定 i d x ( x ) = x − ′ a + 1 idx(x)=x−′a+1 idx(x)=xa+1。 (当然也可以直接用si的ASCII值)

hash函数有许多,这里只介绍常用的BKDR Hash函数

更多hash函数请参考http://blog.chinaunix.net/uid-15014334-id-5612796.html

BKDR Hash函数:

h a s h [ 1 ] = i d x ( s 1 ) hash[1]=idx(s_1) hash[1]=idx(s1) ,在模mod下

h a s h [ i ] = h a s h [ i − 1 ] ∗ p + i d x ( s i ) hash[i]=hash[i-1]*p+idx(s_i) hash[i]=hash[i1]p+idx(si) ,在模mod下

其中p和mod都是我们指定的参数,一般情况下将p和mod尽量取大即可,这样冲突的概率是很低的。

举例:

如取p=13,mod=101,对字符串abc进行Hash

hash[0] = 1

hash[1] = (hash[0] * 13 + 2) % 101 = 15

hash[2] = (hash[1] * 13 + 3) % 101 = 97

都有哪些Hash方法?

普通单Hash方法

只选取一个hash值用来表示一个字符串

自然溢出方法

我们可以将hash[] 的类型定为unsigned long long ,也就是 2 64 − 1 2^{64}-1 2641。并且我们选取$2^{64}-1 $为模数,这样不用每次都进行取模操作( 溢出自动取模) 。这在Acm解决问题是常见的。

双Hash方法

将一个字符串用不同的 m o d mod mod hash两次,将这两个结果用一个二元组表示,作为Hash结果。

双hash方法下hash冲突的概率是很低的。

BKDR Hash函数怎么O(1)获得子串的hash值?

假设有一|S|=5的字符串,设Si为第i个字符,其中1≤i≤5。

根据定义分别求出hash[i]

hash[1]=s1

hash[2]=s1∗p+s2

hash[3]=s1∗p2+s2∗p+s3

hash[4]=s1∗p3+s2∗p2+s3∗p+s4

hash[5]=s1∗p4+s2∗p3+s3∗p2+s4∗p+s5

假设我们现在想获得字串 s 3 s 4 s_3s_4 s3s4的hash值,简单计算我们可以知道 s 3 s 4 s_3s_4 s3s4的hash值为 s 3 ∗ p + s 4 s_3*p+s_4 s3p+s4, 我们再看下上面列出来的公式,咦~,这不是hash[4]的后缀吗!该后缀前面的部分是 h a s h [ 2 ] ∗ p 2 hash[2]*p^2 hash[2]p2 ! ,为此我们发现所有的子串都可以按照这样的方式得出该字串的hash值。

为此我们得出子串 s i . . s j s_i..s_j si..sj 的hash值为: h a s h [ j ] − h a s h [ i − 1 ] ∗ p j − i + 1 hash[j]-hash[i-1]*p^{j-i+1} hash[j]hash[i1]pji+1.

当然因为减法可能会出现负数,为了一致我们将统一处理: ( ( h a s h [ j ] − h a s h [ i − 1 ] ∗ p j − i + 1 ) % m o d + m o d ) % m o d ((hash[j]-hash[i-1]*p^{j-i+1})\%mod+mod)\%mod ((hash[j]hash[i1]pji+1)%mod+mod)%mod

字符串Hash的应用

该部分参考博客https://blog.csdn.net/pengwill97/article/details/80879387#字符串hash入门 ,但是解决方案处不同

题型一

描述
问题:

给两个字符串S1,S2,求S2是否是S1的子串,假设S1的长度为n,S2的长度为为m,求S2在S1中出现的次数

数据范围:1=<|S1|,|S2|<=10000

解法:
可以考虑计算出字符串 s 1 s_1 s1 s 2 s_2 s2的hash数组,然后O(n)处理出所有 s 1 s_1 s1长度的 s 2 s_2 s2子串,如果一个子串的hash值与 s 2 s_2 s2的hash值相等,就判定该子串与 s 2 s_2 s2相等。总的时间复杂度为O(n+m)。(说实话这是kmp模板题)

题型二

描述
问题:给N个单词串,和一个文章串,求每个单词串是否是文章串的子串,并求每个单词在文章中出现的次数。

数据范围:文章串长度:[1, 1 0 5 10^5 105],N个单词串总长:[1, 1 0 6 ​ 10^6​ 106]

解法

​ 其实这是一个AC自动机模板题。

题型三

描述
问题:给两个字符串S1,S2,假设S1的长度为n,S2的长度为为m,求它们的最长公共子串的长度。

数据范围:1=<|S1|,|S2|<=10^5

解法:

​ 考虑二分最长公共字符串长度,对于每一个二分的字符串长度假设为ls,接下来求出 s 2 s_2 s2的所有长度为ls的子串的hash值并放在map中,这个时间复杂度是O(mlogm),然后求出 s 1 s_1 s1的所有长度为ls的子串,这个时间复杂度为O(n),如果有一个子串的hash值在map中出现过,证明他们有一个长度为ls的公共子串,就向大于ls的长度找答案。否则就没有出现过,答案比ls小。总的时间复杂度为O(log(min(n,m))*(n+m)logm) 。如果n等于m的话时间复杂度就是O(nlognlogn)

题型四

问题:给一个字符串S,求S的最长回文子串。

比如abcbbabbc的最长回文子串是cbbabbc,bbabb也是回文串,但不是最长的

数据范围: 1=<|S|<=10^5

解法
manacher算法模板题目
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值