哈希值
在表示字符串时,通常可以使用ASCII码的形式表示成整数形式。在ASCII码表中,共有128个ASCII码。因此,我们可以将一个字符串转化为一个128进制数。
将其转化成128进制数后,为方便在代码中进行储存和运算,可以将这个128进制数转化为十进制数表示。这个就是字符串的哈希值。
若字符串哈希值过大,可以使用取模极大质数的方式储存。经过数学证明,取模大质数出现哈希值重复的概率最小。例如可以取模:1e9+7,998244353等常用大质数.
在求字符串哈希值时,可以使用哈希前缀和的形式。具体实现见下面示例代码。
使用哈希前缀和,可以快速求出子串哈希值。
例如:有字符串abc,a哈希值为a,ab哈希值为a*128+b,abc哈希值为a*128*128+b*128+c,因此,若想要求bc哈希值,使用abc-a哈希值即可。
以下是示例代码:
unsigned long long has(string s){
ll h[100005]={0};
for(int i=0;i<s.size();i++)
h[i]+=h[i-1]*128+s[i];
return h[s.size()-1];
}
题目
求哈希值
题目描述
求输入字符串的哈希值取模998244353的结果。
思路
在求哈希值的过程中,每次都取模998244353以防在计算过程中溢出。
AC代码
查字典
题目描述
输入n,以及n行,每行两个词,第一个词表示意思,第二个词表示单词。
接着输入m,以及m行,每行一个词,表示要查询的词。
输出m行,每行一个词,表示查询词的意思。若查询不到,则输出“eh”
思路
首先将每个词的意思按顺序储存好。然后每个词转为哈希值后按顺序记录。查询时,遍历所有哈希值,若存在相同哈希值,则按编号找到对应意思。若遍历完后仍未查到,则输出eh。
比较哈希值相对于比较字符串来说更省时。
AC代码
消失的密文
题目描述
多组输入,对于每组输入:
第一行输入一个字母表,表示正常字母表对应位置的字母在密文中被替换为输入字母表中字母。
第二行输入不完整一个字符串。它的完整字符串应为:前半段为密文,后半段为明文,两端拼接而成的字符串。
输出完整字符串
思路
设输入字符串长n,密文最短可能为n/2,最长可能为n。因此枚举密文长度k,若后n-k个和前n-k个子串的哈希值相同,则密文为前k个。
这道题还没AC
哈希冲突
生日悖论
一年三百六十五天,求23个人中生日重复的概率是多少。
我们认为概率会很小,但经过程序尝试可得,概率大约为50%左右。因此,哈希算法也是同样的道理,很容易出现冲突。
经过计算,当模数为M,比较n次时,冲突率为:
所以就需要使用更好的方法解决冲突。
双哈希
我们可以使用两个不同底数与模数的哈希,这样可以大大降低哈希的冲突率。
为了方便,可以构造一个哈希结构体。
代码如下:
struct Hash{
ll base,mod,h[N],pw[N];
void init(ll base_in,ll mod_in){
base=base_in,mod=mod_in;
pw[0]=1;
for(int i=1;i<N;i++)pw[i]=pw[i-1]*base%mod;
}//提前计算底数幂
void make(string s){
h[0]=0;
for(int i=1;i<=s.size();i++)h[i]=(h[i-1]*base%mod+s[i])%mod;
}//计算字符串哈希值表
ll get(int l,int r){return (h[r]-h[r-l+1]*pw[r-l+1]%mod+mod)%mod;}
//获取字串哈希
}
开两个Hash类型的变量,分别设置不同的底数与模数。
这样之后,哈希冲突的概率就被降到极低了,就很好地解决了哈希冲突的问题。