你真的可以通过哈希有效地将相同的字符串分组吗?

Posted

技术标签:

【中文标题】你真的可以通过哈希有效地将相同的字符串分组吗?【英文标题】:Can you really divide identical strings into groups efficiently through hashes? 【发布时间】:2022-01-15 07:32:29 【问题描述】:

我正在阅读这篇文章 this article 谈论字符串散列。在 “在字符串数组中搜索重复字符串” 部分,它声称您可以使用字符串散列对时间复杂度为 O(M*N*log(N)) 的相同字符串进行分组。

查看文章中介绍的这个代码示例

vector<vector<int>> group_identical_strings(vector<string> const& s) 
    int n = s.size();
    vector<pair<long long, int>> hashes(n);
    for (int i = 0; i < n; i++)
        hashes[i] = compute_hash(s[i]), i;

    sort(hashes.begin(), hashes.end());

    vector<vector<int>> groups;
    for (int i = 0; i < n; i++) 
        if (i == 0 || hashes[i].first != hashes[i-1].first)
            groups.emplace_back();
        groups.back().push_back(hashes[i].second);
    
    return groups;

我对这段代码如何正确感到非常困惑,因为它仅在hashes[i].first != hashes[i-1].first 的条件下创建一个新组。两个字符串可以不同但具有相同的哈希值,因此即使它们不同,两个字符串也可以添加到同一个组中?这个条件在我看来还不够。

我错了吗?这段代码正确吗?为什么?

如果不是,那么这种算法或至少这种复杂性真的可以实现吗?

【问题讨论】:

这与我们说哈希表具有 amortized O(1) 查找的原因相同。当然,自然会发生冲突,我们将不得不依靠其他一些(可能不是 O(1))过程来区分共享哈希的 2+ 个键。然而,从统计上看,这种情况发生的频率应该足够低,每次碰撞的成员也足够少,我们可以说总体上摊销的时间复杂度仍然是 O((1)。 【参考方案1】:

您非常正确,两个不同的字符串可以具有相等的哈希值。这称为hash collision。但是,归结为您使用的散列函数。有些哈希函数不太可能找到冲突,因此您可以很好地使用该算法而不必担心它会被破坏。在密码学中,我们依赖于加密安全散列函数的这一特性(参见例如here)。

事实上,您提到的消息来源如下:

这是您必须牢记的重要部分。使用散列不会 100% 确定正确,因为两个完全不同的字符串可能具有相同的散列(散列冲突)。然而,在大多数任务中,这可以安全地忽略,因为两个不同字符串的哈希冲突的概率仍然非常小。我们将在本文中讨论一些如何将碰撞概率保持在非常低的技术。

因此,正如您所说,该算法在数学上并不正确。但是通过正确选择散列函数,它在实践中崩溃的概率可以忽略不计。

【讨论】:

以上是关于你真的可以通过哈希有效地将相同的字符串分组吗?的主要内容,如果未能解决你的问题,请参考以下文章

币点学堂:一直挂在嘴边的哈希算法你真的了解吗?

是否有可能获得相同的 SHA1 哈希? [复制]

正确地将字符串添加到 Windows 注册表中的 REG_BINARY 类型

子资源完整性真的有效吗?

Swift中相同字符串的不同md5哈希

C++:检查字符串是不是是有效的 MD5 十六进制哈希