我有一百万个ASCII字符串,没有重复,每个最多7个字节。我需要将每个字符串映射到正整数。这些整数中最大的应该是不超过一百万。虽然初始化可能很慢,但查找应该很快:给定一个字符串,返回相应的int(或-1,如果没有找到)。如何在C ++ 11中实现它?
一种解决方案:将字符串累积为std::unordered_map<string,int>
;然后迭代地图,从递增计数器分配int。然后查找,unordered_map::find("foo")->second
。但它闻起来像其他容器会更快,开销更少(内置索引,而不是手工编码)。也许unordered_set
和指针算术??
范围限制似乎使得完美的哈希变得困难。
(int&#39; s范围受到限制,因为它索引到传递给svm_light的特征向量。该软件不使用稀疏存储,因此具有数万亿(通常为零)元素的向量使它耗尽内存。所以这个字符串到int的预处理类实现了一个稀疏的数据结构。)
答案 0 :(得分:2)
您所描述的内容如perfect hashing。
有些C ++库可以实现完美的哈希,例如Tiny perfect hash library for C, C++, and Lua。
答案 1 :(得分:1)
将字符串转换为int64_t
,将它们存储在unordered_set
中,并将迭代器用作唯一索引。
实际上,您将实现O(1)查找,加上O(N)来计算迭代器偏移量。您还可以保证最大索引不会超过阵列的大小。
unordered_set<int> s;
s.insert(10);
s.insert(2000000);
s.insert(5000000);
int index = std::distance(s.find(10), s.end());
cout << index << endl;
index = std::distance(s.find(2000000), s.end());
cout << index << endl;
index = std::distance(s.find(5000000), s.end());
cout << index << endl;
输出:
1
2
3
现在您已拥有唯一的映射,请使用unordered_map
来实现目标,并放弃unordered_set
:
unordered_set<int> s;
unordered_map<int,int> m;
s.insert(10);
s.insert(2000000);
s.insert(5000000);
int index = std::distance(s.find(10), s.end());
m[10] = index;
cout << index << endl;
index = std::distance(s.find(2000000), s.end());
m[2000000] = index;
cout << index << endl;
index = std::distance(s.find(5000000), s.end());
m[5000000] = index;
cout << index << endl;
s.clear();
cout << m[10] << " " << m[2000000] << " " << m[5000000] << endl;
查找将是O(1)。
答案 2 :(得分:0)
如果你有数百万个字符串,每个字符串长度为7个字节,那么它是使用radix-sort的完美先决条件;所以基本上首先你将所有10 ^ 6个字符串存储在大数组中(它只有7MB / 6.7MiB,所以非常易于管理),然后使用基数排序算法排序 - 时间复杂度 O(wn)在您的情况下,em>, w = 7, n = 10 ^ 6,可以实现 in situ 。实现的细节对于保持线性复杂度的低常数很重要,但是基数排序很容易实现。
作为基数排序的替代方法,您可以简单地将字符串视为uint64_t
并使用std::sort
(它实现了经过优化的内部排序,尽管时间更长,但它可以执行与约束相同的基数复杂性)。
对数组进行排序后,迭代它并将数组索引放入普通std::unordered_map
中,并将字符串作为键。因此,最后您在基本线性时间内创建了完美的散列,最终以平均 O(1)进行反向查找。
[edit]为了将字符串放入unordered_map,你可能想要实现自己的散列算法,我建议使用 djb2 ,它具有良好的统计特性并且易于实现。