我正在尝试为UTF32字符串中的字符分配一个介于0到N之间的唯一索引(其中n是字符串中唯一字符的数量)。
例如,如果我有字符串“ hello”,则函数的输出为:
'h' = 0
'e' = 1
'l' = 2
'o' = 3
字符串“ hello”中有4个唯一字符,因此输出必须在0到3之间。
我知道可以很容易地使用哈希表甚至最小的完美哈希来完成。我很好奇的是,是否有一种更有效的方式来处理此任务,因为我只需要将一个字符映射到一个输出值即可(例如,我不需要对整个字符串进行哈希处理)。因此,使用类似std :: map之类的东西似乎有些过大,但是我找不到能够初始化或评估更快的替代方法(尽管我想您可以将字符推入排序后的数组,然后使用二进制搜索进行查找。
答案 0 :(得分:2)
我可能会使用哈希表(以std::unordered_set
的形式)存储唯一字母,然后在需要输出时仅使用一个简单的计数器。
类似
std::string str = "hello";
std::unordered_set<char> chars(begin(str), end(str));
std::size_t counter = 0;
for (char c : chars)
std::cout << '\'' << c << "' = " << counter++ << '\n';
答案 1 :(得分:1)
任何初始化或评估更快的替代方法
您不会比std::unordered_map<char, size_t>
更快,因为您必须在知道是否需要存储新的char
之前检查是否已经看到char
-> size_t
为其映射。
当然,除非您编写出更好的无序图。正如@MaxLanghof所指出的:这可以通过将{em> {em> 值设置为std::array<char, 256>
之类的东西来完成。
答案 2 :(得分:1)
如果使用8位字符,则可以使用std::array<char, 256>
到唯一索引的char
映射(显然也适合char
):
constexpr unsigned char UNASSIGNED = 255; // Could be another character but then the loop logic gets harder.
std::array<unsigned char, 256> indices;
std::fill(indices.begin(), indices.end(), UNASSIGNED);
std::string input = ...;
unsigned char nextUniqueIndex = 0;
for (unsigned char c : input)
if (indices[c] == UNASSIGNED)
{
indices[c] = nextUniqueIndex;
++nextUniqueIndex;
}
// indices now contains a mapping of each char in the input to a unique index.
这当然要求您的输入字符串不使用char
的整个值范围(或输入中不包含256个不同的字符)。
现在,您说您正在使用UTF32,这不能使该解决方案立即可行。实际上,对于32位字符,映射将需要16 GB的内存(在任何情况下都无法正常运行)。但是,如果您实际上以随机顺序接收到2个 32 个不同的UTF32字符,则您已经具有16 GB的输入数据,因此,此时的问题是“您可以对输入数据做什么样的假设?被利用来改善查找”(大概以良好的哈希函数的形式)以及哪种哈希表可为您提供最佳性能。我敢保证std::unordered_map
在每个键值对中都有单独的分配,并且在查找时会遍历链接列表不会导致最佳性能。
您提到的排序方法就是这样一种选择,但是例如整个输入是两个字符的混合,与其他方法相比,这也不是“有效的”。我还将在此处删除关键字Bloom Filter,因为对于大量数据,这可能是一种快速处理经常出现的字符的好方法(即,针对频繁键与不频繁键具有单独的数据结构)。
答案 3 :(得分:0)
当您使用UTF32字符串时,我认为这是有充分理由的,即您想支持来自世界各地的大量不同字符和符号。如果您根本无法确定您最有可能会处理哪些字符,那么我认为“一些程序员花花公子”的答案是您的最佳选择。
但是,众所周知,std::unordered_set
比简单的数组查找要慢得多,正如Max Langhof所提出的那样。因此,如果您可以做出一些假设,则可以将这两个想法结合起来。
例如,如果您可以合理地假设您的大多数输入将是ASCII字符,则可以使用以下内容:
constexpr char32_t ExpectedStart = U' '; // space == 32
constexpr char32_t ExpectedEnd = 128;
int main()
{
std::basic_string<char32_t> input = U"Hello €";
std::array<bool, ExpectedEnd - ExpectedStart> fastLookup;
std::fill(fastLookup.begin(), fastLookup.end(), false);
std::unordered_set<char32_t> slowLookup;
for (auto c : input)
{
if (ExpectedStart <= c && c < ExpectedEnd)
fastLookup[c - ExpectedStart] = true;
else
slowLookup.insert(c);
}
size_t unique_id = 0;
for (char32_t c = ExpectedStart; c < ExpectedEnd; ++c)
if (fastLookup[c - ExpectedStart])
std::wcout << '\'' << (wchar_t)c << "' = " << unique_id++ << '\n';
for (auto c : slowLookup)
std::wcout << '\'' << (wchar_t)c << "' = " << unique_id++ << '\n';
}
请注意,出于打印目的,我将字符强制转换为wchar_t
,因为显然很难正确打印char32_t
。但是我假设您的最终目标仍然不是打印,所以我希望这没关系。