C ++:关于字符串序列的哈希函数的建议,其中字符串的顺序是无关紧要的

时间:2013-04-01 10:19:40

标签: c++ hash map sequence string-hashing

假设您有这两个字符串序列

abc cba bc

bc abc cba

我正在尝试为这些序列创建一个映射(序列也是一个字符串),以便上面两个序列映射到同一个桶中。

我最初的想法是添加分别应用于每个字符串的散列函数的结果。这样他们的顺序无关紧要。如果我将散列函数作为一个整体应用于序列字符串,那么散列结果当然会有所不同。

然而,我对字符串散列函数的世界很新,我不知道这种方法是否有效。

在本网站http://www.partow.net/programming/hashfunctions/index.html

我发现了很多不同的字符串哈希实现,但是我不确定哪一个对我的需求来说是“最好的”。

序列中每个字符串的一些技术细节是每个字符串不超过25个字符。此外,每个序列的字符串不会超过3个。

问题

1.这种方法是将字符串散列函数的结果添加到序列的每个字符串中吗?

2.如果是,我应该使用哪个字符串哈希函数,这会产生少量的冲突并且也是时间效率的?

提前谢谢

3 个答案:

答案 0 :(得分:2)

只是想法演示(非常低效的字符串复制),复杂度O(NlogN),其中N是键的大小(=== O(1),如果你的键在编译时已知长度不变),我不知道认为你可以做更好的复杂性:

#include <boost/functional/hash.hpp>
#include <set>
#include <algorithm>

std::size_t make_hash(
  std::string const& a,
  std::string const& b,
  std::string const& c)
{
    std::string input[] = {a,b,c};
    std::sort(input, input + (sizeof(input)/sizeof(*input)));
    return boost::hash_range(input, input + (sizeof(input)/sizeof(*input)));
}

#include <iostream>
// g++ -I.../boost_1_47_0 string_set_hash.cpp
int main()
{
    std::cout << make_hash("abc", "bcd", "def") << std::endl; // 46247451276990640
    std::cout << make_hash("bcd", "def", "abc") << std::endl; // 46247451276990640
}

boost / functional / hash.hpp的片段供参考:

template <class T>
inline void hash_combine(std::size_t& seed, T const& v)

{
    boost::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

template <class It>
inline std::size_t hash_range(It first, It last)
{
    std::size_t seed = 0;

    for(; first != last; ++first)
    {
        hash_combine(seed, *first);
    }

    return seed;
}

答案 1 :(得分:0)

无论你选择哪种散列函数,你都需要一个运算符来表示每个散列的最终组合:

  • 交换
  • 缔合

总和,产品和排他性或想到作为整数值的候选者。所以是的,添加会起作用。你仍然会遇到需要解决的无关序列的冲突,所以你需要一个字符串比较函数,但同一组字符串的排列最终会在同一个桶中。

你也可以颠倒操作顺序:首先在字符串中添加字符串(例如,添加“ab”和“cba”变为('a'+'c')('b'+'b')( '\ 0'+'a')带有sum或product的进位传播,因此xor或许是一个有趣的候选者,然后应用哈希函数。您甚至可以在执行这两个操作时将它们组合在一起(伪代码如下):

int hash(string a, string b, string c){
    int r = 0, k;
    int m = max(a.length(), max(b.length(), c.length()));
    for (int i = 0; i < m; i++) {
        k = ( i < a.length()? a[i] : 0) ^
              (i < b.length()? b[i] : 0) ^
              (i < c.length()? c[i] : 0);
        r = hash(r,k);
    }
    return r;
}

使用hash增量散列函数。对于足够大的素数(即大于桶阵列的预期大小)的简单模数应该可以用于正常目的。

一个完全不同(更好的?)解决方案是简单地对序列进行排序(3个条目意味着准恒定时间),然后使用比较函数制作有序映射,将字符串视为3位数字的“数字”。但这超出了问题的范围。

答案 2 :(得分:0)

我会单独散列每个元素。

然后对那些哈希进行排序。排序3 size_t很快。

然后将这些哈希链接起来。您的库可能具有哈希链函数,甚至可以使用带溢出包的hash( a+b+c )

避免使用xor,因为xor两个相同的哈希值为零。并且相同字符串的哈希是相同的。因此,天真的xor可能导致( a,a,b )( c,c,b )具有相同的哈希输出,这很糟糕。