字符串Hash

        (基于进制的)字符串Hash本质是用一个数字代表一个字符串,从而降低字符串处理的复杂度。这是一个很大的数字,采用unsigned long long类型,可以自然溢出,也简化了取模的过程。

        还需要一个进制数base,用于数字的进制。(base选取质数如131,1331......)

        Hash数组h[ i ]表示字符串1~i的hash值,采用类似前缀和的形式求出任意子集的Hash值

        对于学习过前缀和的朋友来说比较好理解,字符串Hash的作用基本相同。

        这里建议各位先去看一下本人另一篇关于前缀和的blog。https://blog.youkuaiyun.com/lebrce/article/details/145243013https://blog.youkuaiyun.com/lebrce/article/details/145243013        

一、Hash数组的初始化

using unsigned long long = ull;
const ull base=131;
ull h[N];
char s[N];

cin>>s+1;
int n=strlen(s);
for(int i=1;i<n;i++){
    h[i] = h[i-1]*base + s[i];
}

二、Gethash函数

ull Gethash(ull h[],int l,int r){
    return h[r]-h[l-1]*b[r-l+1]    //b[i]为base的幂,需预处理
}

 三、利弊

        很明显字符串Hash的理解难度并不大(比起KMP自然是),但是由于Hash值及其大会需要更大的空间。

四、例题

小 Z 同学每天都喜欢斤斤计较,今天他又跟字符串杠起来了。

他看到了两个字符串 S1 S2 ,他想知道 S1 在 S2 中出现了多少次。

现在给出两个串 S1,S2(只有大写字母),求 S1 在 S2 中出现了多少次。

输入描述

共输入两行,第一行为 S1,第二行为 S2。

1<len(s1)<len(s2)<10^6,字符只为大写字母或小写字母。

输出描述

输出一个整数,表示 S1 在 S2 中出现了多少次。

#include <iostream>
#include <cstring>

// 定义无符号长整型别名
typedef unsigned long long ull;

// 哈希基数
const ull base = 131;
// 数组最大长度
const int N = 1e6 + 9;

// 定义字符串数组
char p[N], s[N];
// 存储基数的幂、字符串 p 和 s 的哈希值
ull b[N], h1[N], h2[N];

// 计算指定区间的哈希值
ull Gethash(ull h[], int l, int r) {
    return h[r] - h[l - 1] * b[r - l + 1];
}

int main() {
    // 输入字符串 p 和 s,从下标 1 开始存储
    std::cin >> (p + 1);
    int m = strlen(p + 1);
    std::cin >> (s + 1);
    int n = strlen(s + 1);

    // 初始化基数的 0 次幂为 1
    b[0] = 1;

    // 计算字符串 p 的哈希值
    for (int i = 1; i <= m; i++) {
        h1[i] = h1[i - 1] * base + (ull)p[i];
    }

    // 计算字符串 s 的哈希值和基数的幂
    for (int i = 1; i <= n; i++) {
        h2[i] = h2[i - 1] * base + (ull)s[i];
        b[i] = b[i - 1] * base;
    }

    // 统计字符串 p 在字符串 s 中出现的次数
    int ans = 0;
    for (int i = 1; i + m - 1 <= n; i++) {
        if (Gethash(h1, 1, m) == Gethash(h2, i, i + m - 1)) {
            ans++;
        }
    }

    // 输出结果
    std::cout << ans << std::endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值