字符串哈希

文章目录

字符串哈希

定义

hash,其实就是将一个东西映射成另一个东西,类似Map,key对应value。

那么字符串Hash,其实就是:构造一个数字使之唯一代表一个字符串。将映射关系进行一一对应,也就是一个字符串对应一个数字,那么一个数字也对应一个字符串。

用字符串Hash的目的是,我们如果要比较一个字符串,我们不直接比较字符串,而是比较它对应映射的数字,这样子就知道两个"子串"是否相等。从而达到,子串的Hash值的时间为 O(1),进而可以利用"空间换时间"来节省时间复杂度。

构造字符串哈希

将字符串映射成数字,和我们平时的将一个某Base进制数,变为一个十进制数相类似。

介绍两种方法,每一种方法,主要都是用使用 Base 和 Mod(都要求是素数),在这里我们要尽量把MOD和Base取大。这种情况下,冲突(即不同字符串却有着相同的hash值)的概率是很低的。

常用Base:127,131,13331

常用Mod:1e9+7,1e9+9,19260817,998244353

下文的约定

cpp 复制代码
#define int long long

const int N=1e5+10,base=13331,mod=1e9+7;
int h[N], p[N];
h[0] = 0;
p[0] = 1;

这里的 h a s h hash hash 数组表示子串 [ 0 , i ] [0, i] [0,i] 的哈希值, p [ i ] p[i] p[i] 表示 B a s e i Base^i Basei,所以把 h a s h [ 0 ] hash[0] hash[0] 初始化为 0, p [ 0 ] p[0] p[0] 初始化为 1。

假如给你一个数字1166,形式上你只知道它只是1和6的组合,但你知道它代表的实际大小为 1 ∗ 10 3 + 1 ∗ 10 2 + 6 ∗ 10 1 + 6 ∗ 10 0 1*10^3+1*10^2+6*10^1+6*10^0 1∗103+1∗102+6∗101+6∗100

同理,给你一个字符串,要把它转换为数字,就可以先把每一个字符都先对应一个数字,然后把它们按照顺序乘以进制(Base)的幂进行相加,然后这个数可能很大,所以一般会取余数(MOD)。

单哈希

单哈希只对字符串做一次哈希,其递推公式为:
h [ i ] = ( h [ i − 1 ] × B a s e + s [ i ] ) ( m o d M O D ) h[i] = (h[i - 1] \times Base + s[i]) \pmod{MOD} h[i]=(h[i−1]×Base+s[i])(modMOD)

我们通过迭代的方式就可以算出每一个子串的哈希值了。

获取子串

我们得到的 Hash值都是前 i 个字符的字串,利用前缀Hash值,我们可以O(1) 时间来获取某个 [ l , r ] [l,r] [l,r] 字串。
h [ l , r ] = h [ r ] − h [ l − 1 ] × p r − l + 1 h[l,r]=h[r]-h[l-1] \times p^{r-l+1} h[l,r]=h[r]−h[l−1]×pr−l+1

模板题:字符串哈希

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,base=13331,mod=1e9+7;
int h[N],p[N],n,m;
string s;
int ask(int l,int r){
    return (h[r]-(h[l-1]*p[r-l+1]%mod)+mod)%mod;
}
void solve() {
    cin>>n>>m>>s;
    s='#'+s;
    p[0]=1;
    for(int i=1;i<=n;i++){
        h[i]=(h[i-1]*base+s[i])%mod;
        p[i]=(p[i-1]*base)%mod;
    }
    while(m--){
        int l1,r1,l2,r2;
        cin>>l1>>r1>>l2>>r2;
        if(ask(l1,r1)==ask(l2,r2)){
            cout<<"Yes"<<'\n';
        }else{
            cout<<"No"<<'\n';
        }
    }
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--) solve();
    return 0;
}

Hash重要应用:最长回文子串

枚举每个字符为回文中心,判断正反向哈希是否相等来判断是否是回文,由于有了哈希可以快速判断子串是否相等,对半径使用二分法搜索,时间复杂度 O(nlogn)

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,base=13331,mod=1e9+7;
int h[N],p[N],n,h1[N];
string s,t;
int ask(int l,int r){
    return (h[r]-(h[l-1]*p[r-l+1]%mod)+mod)%mod;
}
int ask1(int l,int r){
    return (h1[r]-(h1[l-1]*p[r-l+1]%mod)+mod)%mod;
}
void solve() {
    getline(cin,s);
    n=s.size();
    t=s;
    reverse(t.begin(),t.end());
    t='#'+t;
    s='#'+s;
    p[0]=1;
    for(int i=1;i<=n;i++){
        h[i]=(h[i-1]*base+s[i])%mod;
        h1[i]=(h1[i-1]*base+t[i])%mod;
        p[i]=(p[i-1]*base)%mod;
    }
    int ans=1;
    for(int i=1;i<=n;i++){
        int l=0,r=min(i-1,n-i);
        while(l<=r){
            int mid=l+r>>1;
            if(ask(i-mid,i-1)==ask1(n-(i+mid)+1,n-(i+1)+1)){
                l=mid+1;
                ans=max(ans,2*mid+1);
            }else{
                r=mid-1;
            }
        }
        l=0,r=min(i,n-i);
        while(l<=r){
            int mid=l+r>>1;
            if(ask(i-mid+1,i)==ask1(n-(i+mid)+1,n-(i+1)+1)){
                l=mid+1;
                ans=max(ans,2*mid);
            }else{
                r=mid-1;
            }
        }
    }
    cout<<ans<<'\n';
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
//    cin>>T;
    while(T--) solve();
    return 0;
}

双哈希

双哈希就是用两个模数和两个素数对一个字符串算两次哈希,得到一个pair,用pair做比较,避免哈希碰撞。

相关推荐
石油人单挑所有2 分钟前
基于多设计模式下的同步&异步日志系统测试报告
服务器·c++·vscode·设计模式
人道领域9 分钟前
【LeetCode刷题日记】225.用队列实现栈--三招实现栈操作(多种思维)
java·开发语言·算法·leetcode·面试
新新学长搞科研19 分钟前
【高届数机械工程会议】第十二届机械工程、材料和自动化技术国际学术会议(MMEAT 2026)
运维·人工智能·算法·机器学习·自动化·软件工程·激光
狐璃同学29 分钟前
数据结构(2)线性表
数据结构·算法
啦啦啦_999934 分钟前
4. KNN算法之 特征预处理(归一化&标准化)
算法
淘气包海鸟1 小时前
雷达基本原理
算法·信息与通信
Tisfy1 小时前
LeetCode 2615.等值距离和:分组(哈希表+前缀和)
算法·leetcode·散列表
小此方1 小时前
Re:从零开始的 C++ 进阶篇(四)工业级 C++ 编程:如何构建异常安全的健壮系统?(含案例分析)
运维·开发语言·c++·安全
电商API_180079052471 小时前
如何实现批量化自动化获取淘宝商品详情数据?爬虫orAPI?
大数据·c++·爬虫·自动化
t***5441 小时前
如何确认 Clang 是否在 Dev-C++ 中成功应用
java·开发语言·c++