首页 > 编程语言 >字符串哈希算法

字符串哈希算法

时间:2023-06-14 10:37:24浏览次数:42  
标签:... hash limits sum mid 算法 哈希 字符串

问题描述

考虑 1044. 最长重复子串 (Hard),本题思路并不难,可以使用二分答案来解决,假设答案为 mid,那么长度大于 mid 的子串在 s 中只会出现一次,否则至少出现两次。

因此只需要考虑子串在 s 中的出现次数即可,比较直接的想法是使用 key 为 stringunordered_map,然而 unoredere_map 自带的哈希函数,其时间复杂度和空间复杂度都很高,为 $O(len)$,因此,需要一个简单一点的哈希函数。

字符串哈希

参照宫水三叶大佬的 字符串哈希

我们需要使用一个比字符串 s 略长的哈希数组 vector<int> h(s.size() + 10),以及次方数组 vector<int> p(s.size() + 10)。 对长度为 len 的数组,只需要利用前缀和思想 h[i + len] - h[i] * p[len] 即可在 $O(1)$ 时间内计算出哈希值。

其中 p[0] = 1h[i] = h[i - 1] * P + s[i - 1]p[i] = p[i - 1] * P

$P$ 可以依次取 $131,\ 13131,\ 1313131$ 等,出现哈希碰撞就考虑取更大的质数。

其所使用的哈希函数计算方法本质为:$hash(s) = \sum\limits_{i = 1}^{l} s[i - 1] * P^{l - i}$(其中 $l$ 是字符串 $s$ 的长度)。

对这个前缀和计算公式作一个简单证明: $hash(s[1...r]) = \sum\limits_{i = 1}^{r}s[i - 1] * P^{r - i},hash(s[1...l]) = \sum\limits_{i = 1}^{l}s[i - 1]*P^{l - i}$

$hash(s[1...r]) -P^{r - l} * hash(s[1...l]) = \sum\limits_{i = 1}^{l}s[i - 1]P^{r-l+l-i} +\sum\limits_{i=l+1}^{r}s[i - 1] P^{r-i}-P^{r-l}*\sum\limits_{i = 1}^{l}s[i - 1]P^{l - i} = \sum\limits_{i=l+1}^{r}s[i - 1] P^{r-i}$

而 $hash(s[l + 1...r]) = \sum\limits_{i = l}^{r}s[i - 1] * P^{r - i}$

即 $hash(s[1...r]) - hash(s[1...l]) * P^{r - l} = hash(s[l + 1...r])$

代码

class Solution {
  public:
    string check(int mid, string &s, vector<uint64_t> &h, vector<uint64_t> &p) {
        unordered_set<uint64_t> substrs;
        for (int i = 0; i + mid <= s.size(); ++i) {
            int j = i + mid;
            long hash = h[j] - h[i] * p[j - i];
            if (substrs.find(hash) != substrs.end()) {
                return s.substr(i, mid);
            }
            substrs.insert(hash);
        }
        return "";
    }
    string longestDupSubstring(string s) {
        // 二分查找答案,左边界为0,右边界为 s.size(),左闭右开
        unordered_map<string, int> substr;
        int left = 0, right = s.size();
        string ans;
        int P = 1313131, n = s.size();
        vector<uint64_t> h(n + 10);
        vector<uint64_t> p(n + 10);
        p[0] = 1;
        for (int i = 0; i < n; ++i) {
            p[i + 1] = p[i] * P;
            h[i + 1] = h[i] * P + s[i];
        }
        while (left < right) {
            int mid = left + (right - left) / 2;
            string tmp_res = check(mid, s, h, p);
            if (!tmp_res.empty()) {
                left = mid + 1;
            } else {
                right = mid;
            }
            ans = ans.size() < tmp_res.size() ? tmp_res : ans;
        }
        return ans;
    }
};

标签:...,hash,limits,sum,mid,算法,哈希,字符串
From: https://www.cnblogs.com/zwyyy456/p/17479467.html

相关文章

  • 快速选择算法
    问题描述给定一个长度为$n$的数组,如何在$O(n)$的时间复杂度内找到第$k$大的数。思路朴素的想法是先排序,然后直接找到第$k$个元素,时间复杂度为$O(n\logn)$。我们可以利用快速排序的思想来解决这个问题,考虑快速排序的划分过程,在快速排序的“划分”结束后,数组$A_p\cdotsA_r$被......
  • LRU 算法与 LFU 算法
    算法介绍LRULRU全称是LeastRecentlyUsed,即最近最久未使用算法。LRU根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高,它是页面置换算法的一种,也常用于缓存设计。LFULFU全称是LeastFrequentlyUsed,根据频率来选择要......
  • kmp 算法
    问题描述kmp算法解决的是字符串匹配问题,即:字符串P是否是字符串S的子串?如果是,它出现在s的哪些位置?这里我们称S为主串,P为模式串。思路首先是暴力匹配算法(Brute-Force算法),代码如下:voidBruteForce(strings,stringp){intlen_s=s.size(),len_p=p.size();fo......
  • 【基础算法】单链表的OJ练习(2) # 链表的中间结点 # 链表中倒数第k个结点 #
    前言对于单链表的OJ练习,<fontcolor=bluesize=4>需要深刻理解做题的思路</font>,这样我们才能够在任何场景都能够熟练的解答有关链表的问题。关于OJ练习(1):==->==传送门==<-==,其题目较为简单,思路也好理解,本章与(1)差不多,难度不大,<fontcolor=orangesize=4>核心点就在于理解解......
  • 马拉车算法
    截图来自董老师https://www.bilibili.com/video/BV173411V7Ai/?spm_id_from=333.999.0.0&vd_source=23dc8e19d485a6ac47f03f6520fb15c2  P3805【模板】manacher算法#include<iostream>#include<cstring>#include<cmath>#include<algorithm>#include......
  • 【VRP问题】基于遗传算法求解多约束多无人机灾情应急救援路径规划问题附matlab代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 多种智能优化算法运行时间和不同函数测试对比附matlab代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 《数据结构与算法》之二叉树(补充树)
    一.树结构之二叉树操作二叉树的查找二叉搜索树,也称二叉排序树或二叉查找树二叉搜索树:一棵二叉树,可以为空,如果不为空,应该满足以下性质:非空左子树的所有结点小于其根结点的键值非空右子树的所有结点大于其根结点的键值左右子树都是二叉搜索树对于二叉树的查找,其实沿用的是......
  • 文心一言 VS 讯飞星火 VS chatgpt (40)-- 算法导论5.4 4题
    四、一次聚会需要邀请多少人,才能让其中3人的生日很可能相同?文心一言:为了解决这个问题,我们需要先计算出所有可能的生日组合数,然后计算出其中生日相同的组合数。已知需要邀请的人数为:23人根据组合数公式,可计算出所有可能的生日组合数:2.58520167*10^{22}根据生日相同的组合......
  • json字符串解析 多语言替换
    importlombok.extern.slf4j.Slf4j;importorg.apache.commons.collections4.MapUtils;importorg.apache.commons.lang3.StringUtils;importorg.springframework.beans.factory.annotation.Value;importjava.util.HashSet;importjava.util.List;importjava.util.M......