使用C++ unordered_set检查Isogram单词及自定义哈希器技术咨询
首先,先明确你的核心目标:检查一个单词是否是Isogram(即所有字母不重复,不区分大小写),你选择用unordered_set/unordered_multiset的思路是可行的,但代码里有几个需要修正和优化的点,下面一步步拆解:
1. 修正模板参数的语法错误
你代码里的unordered_multiset<char, CustomHasher...是不完整的,unordered_multiset的标准模板参数格式是:
template <class Key, class Hash = hash<Key>, class KeyEqual = equal_to<Key>, class Allocator = allocator<Key>> class unordered_multiset;
如果要指定自定义哈希器,只需要传CustomHasher作为第二个参数即可(后续参数用默认值的话可以省略),比如:
unordered_multiset<char, CustomHasher> my_multiset;
2. 选择更合适的容器:用unordered_set替代unordered_multiset
检查Isogram的核心是判断是否有重复字符,unordered_set的特性是自动去重,插入时如果元素已存在会返回插入失败,正好适配这个场景——不需要统计重复次数,只要发现重复就可以直接判定不是Isogram。用unordered_multiset反而需要额外调用count()方法检查存在性,效率更低。
3. 完善自定义哈希与相等性判断
你的CustomHasher已经实现了把字符转成小写后映射到0-25的哈希值,但这里有个隐藏问题:默认的KeyEqual会区分大小写(比如'A'和'a'会被视为不同的键),所以光靠哈希函数转小写还不够,需要同时自定义KeyEqual来忽略大小写,或者在插入容器前统一把所有字符转成小写/大写。
方案A:自定义哈希+自定义相等性判断
#include <unordered_set> #include <string> #include <cctype> struct CustomHasher { size_t operator()(const char& c) const { // 用unsigned char避免tolower处理负数导致未定义行为 return tolower(static_cast<unsigned char>(c)) - 'a'; } }; struct CaseInsensitiveEqual { bool operator()(const char& a, const char& b) const { return tolower(static_cast<unsigned char>(a)) == tolower(static_cast<unsigned char>(b)); } }; // 封装成易用的容器类型 using CaseInsensitiveSet = std::unordered_set<char, CustomHasher, CaseInsensitiveEqual>;
方案B:插入前统一转小写(更简洁)
其实不需要自定义哈希,只要在插入前把字符转成小写,直接用默认的unordered_set<char>即可,代码更简洁:
#include <unordered_set> #include <string> #include <cctype> bool isIsogram(const std::string& s) { std::unordered_set<char> seen; for (char c : s) { char lower_c = tolower(static_cast<unsigned char>(c)); // 可选:跳过非字母字符(根据需求调整) // if (!isalpha(static_cast<unsigned char>(lower_c))) continue; if (!seen.insert(lower_c).second) { // 插入失败,说明字符已存在 return false; } } return true; }
4. 性能优化技巧
- 提前快速判断:如果输入字符串的长度超过26(英文字母的数量),直接返回
false——因为不可能有超过26个不重复的英文字母。 - 遍历提前终止:一旦发现重复字符就立刻返回,不需要遍历完整个字符串。
- 用数组替代哈希表(更高效):因为字母只有26个,用一个大小为26的布尔数组标记是否出现过,时间复杂度O(n),空间复杂度O(1),比
unordered_set更快(避免哈希表的额外开销):
bool isIsogram(const std::string& s) { if (s.size() > 26) return false; bool seen[26] = {false}; for (char c : s) { char lower_c = tolower(static_cast<unsigned char>(c)); // 可选:跳过非字母字符 if (!isalpha(static_cast<unsigned char>(lower_c))) continue; int idx = lower_c - 'a'; if (seen[idx]) { return false; } seen[idx] = true; } return true; }
5. 原代码后续编写建议(若坚持用unordered_multiset)
如果你需要统计重复次数等场景必须用unordered_multiset,完整的代码示例如下:
#include <unordered_multiset> #include <string> #include <cctype> struct CustomHasher { size_t operator()(const char& c) const { return tolower(static_cast<unsigned char>(c)) - 'a'; } }; struct CaseInsensitiveEqual { bool operator()(const char& a, const char& b) const { return tolower(static_cast<unsigned char>(a)) == tolower(static_cast<unsigned char>(b)); } }; bool isIsogram(const std::string& s) { std::unordered_multiset<char, CustomHasher, CaseInsensitiveEqual> char_counts; for (char c : s) { // 可选:跳过非字母字符 if (!isalpha(static_cast<unsigned char>(c))) continue; char_counts.insert(c); if (char_counts.count(c) > 1) { return false; } } return true; }
内容的提问来源于stack exchange,提问作者salim.p

