LeetCode 2156. 查找给定哈希值的子串

本文介绍了一种利用哈希值快速找到字符串中满足特定条件的子串的方法。通过计算子串的哈希值并利用滚动哈希技巧,可以在O(n)的时间复杂度内解决LeetCode上的2156题。文章提供了C语言的解题代码,并强调了如何将问题转化为后缀和求解。此外,还鼓励读者通过刷题提升算法能力,并给出了相关的算法学习资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、题目

1、题目描述

  给定整数 p p p m m m ,一个长度为 k k k 且下标从 0 0 0 开始的字符串 s s s 的哈希值按照如下函数计算:
h a s h ( s , p , m ) = ( v a l ( s [ 0 ] ) ∗ p 0 + v a l ( s [ 1 ] ) ∗ p 1 + . . . + v a l ( s [ k − 1 ] ) ∗ p k − 1 ) m o d    m hash(s, p, m) = (val(s[0]) * p^0 + val(s[1]) * p^1 + ... + val(s[k-1]) * p^{k-1}) \mod m hash(s,p,m)=(val(s[0])p0+val(s[1])p1+...+val(s[k1])pk1)modm
  其中 v a l ( s [ i ] ) val(s[i]) val(s[i]) 表示 s [ i ] s[i] s[i] 在字母表中的下标,从 v a l ( ′ a ′ ) = 1 val('a') = 1 val(a)=1 v a l ( ′ z ′ ) = 26 val('z') = 26 val(z)=26 。给你一个字符串 s s s 和整数 p o w e r power power m o d u l o modulo modulo k k k h a s h V a l u e hashValue hashValue。请你返回 s s s 中 第一个 长度为 k k k 的 子串 s u b sub sub ,满足 hash(sub, power, modulo) == hashValue。测试数据保证一定 存在 至少一个这样的子串。
  样例输入: nums = [0,0,1,0]
  样例输出: [2,4]

2、基础框架

  • C语言 版本给出的基础框架代码如下:
char * subStrHash(char * s, int power, int modulo, int k, int hashValue){
}

3、原题链接

LeetCode 2156. 查找给定哈希值的子串

二、解题报告

1、思路分析

   ( 1 ) (1) (1) v a l ( s [ i ] ) val(s[i]) val(s[i])化简为 g ( i ) g(i) g(i)
   ( 2 ) (2) (2) 对于长度为 k的字符串 s [ 0 : k − 1 ] s[0:k-1] s[0:k1] 的哈希值是:
h ( 0 , k − 1 ) = ( g ( 0 ) p 0 + g ( 1 ) p 1 + . . . + g ( k − 1 ) p k − 1 )   m o d   m h(0, k-1) = (g(0) p^0 + g(1) p^1 + ... + g(k-1) p^{k-1}) \ mod \ m h(0,k1)=(g(0)p0+g(1)p1+...+g(k1)pk1) mod m
   ( 3 ) (3) (3) 对于一个从 i i i 开始,长度为 k k k 的字符串 s [ i : i + k − 1 ] s[i:i+k-1] s[i:i+k1],它的哈希值等于:
h ( i , i + k − 1 ) = ( g ( i ) p 0 + . . . + g ( i + k − 1 ) p k − 1 )   m o d   m h(i, i+k-1) = (g(i) p^0 + ... + g(i+k-1) p^{k-1}) \ mod \ m h(i,i+k1)=(g(i)p0+...+g(i+k1)pk1) mod m
   ( 4 ) (4) (4) 则从 i + 1 i+1 i+1 开始,长度为 k k k 的字符串 s [ i + 1 : i + k ] s[i+1:i+k] s[i+1:i+k],它的哈希值等于:
h ( i + 1 , i + k ) = ( g ( i + 1 ) p 0 + . . . + g ( i + k ) p k − 1 )   m o d   m h(i+1, i+k) = (g(i+1) p^0 + ... + g(i+k) p^{k-1}) \ mod \ m h(i+1,i+k)=(g(i+1)p0+...+g(i+k)pk1) mod m
   ( 5 ) (5) (5) 对相邻两个字符串的哈希值进行处理,得到:
h ( i + 1 , i + k ) p − h ( i , i + k − 1 ) = g ( i + k ) p k − g ( i ) h(i+1, i+k)p - h(i, i+k-1) = g(i+k) p^{k} - g(i) h(i+1,i+k)ph(i,i+k1)=g(i+k)pkg(i)
   ( 6 ) (6) (6) 移项(仔细仔细),得到:
h ( i , i + k − 1 ) = h ( i + 1 , i + k ) p − g ( i + k ) p k + g ( i ) h(i, i+k-1) = h(i+1, i+k)p - g(i+k) p^{k} + g(i) h(i,i+k1)=h(i+1,i+k)pg(i+k)pk+g(i)
   ( 7 ) (7) (7) 转化成后缀和求解,对于长度为 n n n 的字符串,初始情况就是 h ( n − k , n − 1 ) h(n-k, n-1) h(nk,n1)

2、时间复杂度

   O ( n ) O(n) O(n)

3、代码详解

int g(char c) {
    return (c - 'a' + 1);
}

char * subStrHash(char * s, int power, int modulo, int k, int hashValue){
    int i;
    int n = strlen(s);
    int idx;
    long long sum = 0;
    long long p[100005] = {1};
    char *ret = (char *)malloc( sizeof(char) * 100005 );
    int size = 0;
    for(i = 1; i <= k; ++i) {
        p[i] = p[i-1] * power % modulo;
    }

    // 先求 h(n-k, n-1)
    for(i = n-k; i < n; ++i) {
        sum += p[i-n+k] * g(s[i]);
        sum %= modulo;
        if(sum == hashValue) {
            idx = n-k;
        }
    }


    // h(i, i+k-1) = h(i+1, i+k)p  - g(i+k) p^{k} + g(i)
    for(i = n-k-1; i >= 0; --i) {
        sum = sum * p[1] - p[k] * g(s[i+k]) + g(s[i]);
        sum = (sum % modulo + modulo) % modulo;
        // printf("sum = %lld\n", sum);
        if(sum == hashValue) {
            idx = i;
        }
    }

    for(i = idx; i < idx + k; ++i) {
        ret[size++] = s[i];
    }
    ret[size] = '\0';
    return ret;

}

三、本题小知识

  利用相邻字符串哈希值可以递推计算,减少每个子串哈希值计算的时间复杂度。


四、加群须知

  相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」
  那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:

🌌《算法入门指引》🌌

  如果链接被屏蔽,或者有权限问题,可以私聊作者解决。

  大致题集一览:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述



在这里插入图片描述


  为了让这件事情变得有趣,以及「 照顾初学者 」,目前题目只开放最简单的算法 「 枚举系列 」 (包括:线性枚举、双指针、前缀和、二分枚举、三分枚举),当有 一半成员刷完 「 枚举系列 」 的所有题以后,会开放下个章节,等这套题全部刷完,你还在群里,那么你就会成为「 夜深人静写算法 」专家团 的一员。
  不要小看这个专家团,三年之后,你将会是别人 望尘莫及 的存在。如果要加入,可以联系我,考虑到大家都是学生, 没有「 主要经济来源 」,在你成为神的路上,「 不会索取任何 」
  🔥联系作者,或者扫作者主页二维码加群,加入刷题行列吧🔥


🔥让天下没有难学的算法🔥

C语言免费动漫教程,和我一起打卡!
🌞《光天化日学C语言》🌞

让你养成九天持续刷题的习惯
🔥《九日集训》🔥

入门级C语言真题汇总
🧡《C语言入门100例》🧡

组团学习,抱团生长
🌌《算法零基础100讲》🌌

几张动图学会一种数据结构
🌳《画解数据结构》🌳

竞赛选手金典图文教程
💜《夜深人静写算法》💜
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值