下面介绍的字符串 函数把一个任意长度的字符串映射成一个非负整数,并且其冲突概率几乎为零。
取一固定值 ,把字符串看做 进制数,并分配一个大于零的数值,代表每种字符。 一般来说,我们分配的数值都远小于 。例如,对于小写字母构成的字符串,可以令 。
取一固定值 ,求出该 进制数对 的余数,作为该字符串的 值。
通常来说,根据“生日悖论”,在采取单质数作为模数时,元素个数不能超过 个。而采用多个大质数为模数时,元素个数则不能超过 个。而这一数量级远远超过了算法竞赛中可能的规模,因此我们可以认为多模数时是不会发生冲突的。所以,可以认为哈希值相同的字符串就是相同的。
一般来说,我们取 或 ,此时 值产生冲突的概率极低,只要 值相同,我们就可以认为原字符串是相等的。通常我们取 ,即直接使用 类型存储这个 值,在计算时不处理算术溢出问题,产生溢出时相当于自动对 取模,这样可以避免低效的取模 运算。
除了在极特殊构造的数据上,上述 算法很难产生冲突,一般情况下上述 算法完全可以出现在题目的标准解答中。我们还可以多取一些恰当的 和 的值(例如大质数),多进行几组 运算,当结果都相同时才认为原字符串相等,就更加难以构造出使这个 产生错误的数据。
对字符串的各种操作,都可以直接对 进制数进行算术运算反映到 值上。
如果我们已知字符串 的 值为 ,那么在 后添加一个字符 构成的新字符串 的 值就是。其中乘
就相当于 进制下的左移运算, 是我们为 选定的代表数值。
如果我们已知字符串 的 值为 ,字符串 的 值为 ,那么字符串 的 值 。这就相当于通过 进制下在 后边补 的方式,把 左移到与 的左端对齐,然后二者相减就得到了 。
例如,则:
表示为 进制数:1 2 3
表示为 进制数:1 2 3 24 25 26
在 进制下左移 位:1 2 3 0 0 0
二者相减就是 表示为 进制数:24 25 26
根据上面两种操作,我们可以通过 的时间预处理出字符串所有前缀 值,并在 的时间内查询他的任意子串的 值。
模数:
进制数:
code:
ull hashh(char s[]){
int l=strlen(s);
ull num=0;
for(int i=0;i<l;++i)
num=num*base+(ull)s[i];
return num&0x7fffffff;
}
code:
ull f[maxn],bas[maxn];
for(int i=1;i<=n;++i)
{
f[i]=f[i-1]*base+s[i]-'a'+1;
bas[i]=bas[i-1]*base;
}
bool check(int l1,int r1,int l2,int r2)
{
return f[r1]-f[l1-1]*bas[r1-l1+1]==f[r2]-f[l2-1]*bas[r2-l2+1];
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】