为字符串中的每个字符(不是字符位置!)分配唯一的索引
Assigning a unique index to each character (not character position!) within a string
我正在尝试为 UTF32 字符串中的字符分配一个介于 0 和 N 之间的唯一索引(其中 n 是字符串中唯一字符的数量)。
例如,如果我有字符串"hello",则该函数的输出将是:
'h' = 0
'e' = 1
'l' = 2
'o' = 3
字符串"hello"中有 4 个唯一字符,因此输出需要介于 0 和 3 之间。
我知道这可以很容易地使用哈希表来完成,甚至是最小的完美哈希。我很好奇是否有更有效的方法来处理此任务,因为我只需要将单个字符映射到单个输出值(例如,我不需要散列整个字符串)。正因为如此,使用 std::map 之类的东西似乎有点矫枉过正,但是我无法找到任何可以更快地初始化或评估的替代方案(尽管我想你可以将字符推到排序数组中并使用二进制搜索查找它们)。
我可能会使用哈希表(以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';
初始化或评估速度更快的替代方案
你不会比std::unordered_map<char, size_t>
更快,因为你必须检查你是否已经看到了一个char
,然后你才知道你是否需要存储一个新的char
->size_t
地图。
当然,除非你写了一个更好的无序图。 正如@MaxLanghof指出的那样:这可以通过像std::array<char, 256>
一样的东西来完成,该被告知未找到的值。
如果您使用 8 位字符,您可以使用从char
到唯一索引std::array<char, 256>
映射(这显然也适合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,因为对于大量数据,这可能是快速处理常见字符的好方法(即为常用键与不常用键具有单独的数据结构)。
当您使用 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
。但我假设你的最终目标无论如何都不是打印,所以我希望这无关紧要。
- 我的目标是编写一个程序来计算和存储字符串在字符数组中出现的位置
- C++指针数组到字符数组中的特定位置
- 计算在同一位置至少包含一个常用字符的不同字符串对
- 我如何知道字符串中字符相对于英文字母的位置值?
- 我正在尝试为字符打印一些相应的值,但条件总是转到其他位置
- 递增 'int i = 0' 以在初始化的字符数组的位置递增
- 反转字符串中单词的位置,而不更改 O(1) 空格限制中特殊字符的顺序
- 如何计算 3d 数组中的索引值在内存中的位置?如何计算字符**中的索引值在内存中的位置?
- 如何从输入字符串中提取特定位置的字符?
- 如何在 Clang AST 中的源位置之后找到字符的源位置?
- 从输入 1 查找输入 2 中字符的位置
- 为字符串中的每个字符(不是字符位置!)分配唯一的索引
- 提取字符后返回到文件中的特定位置
- 从文件对象的当前位置删除字符
- 访问字符阵列中不可用的内存位置(超出范围值)
- 使用指针在字符串中获取字符的位置
- 如何使用 antlr4 恢复特定行和字符位置的 ParserRuleContext
- 创建一个从前缀到C 中字符位置的新字符串
- c++字符串交换字符位置
- 从文本控件中检索字符位置的客户端区域坐标