同构字符串指两串存在双射映射,模式一致;需用两个哈希表(或ASCII下两个26数组)维护s→t和t→s双向映射,遍历时检查冲突,长度不等则false。

什么是同构字符串,怎么快速判断
两个字符串 s 和 t 同构,当且仅当存在一个双射(一一对应)映射,把 s 中每个字符唯一映射到 t 中某个字符,且该映射可逆。比如 "egg" 和 "add" 是同构的(e→a, g→d),但 "foo" 和 "bar" 不是(o→a 和 o→r 冲突)。
关键不是字符相等,而是「模式一致」:相同位置的字符是否在各自串中重复出现的规律一致。
用两个哈希表维护双向映射关系
单向映射(只记录 s[i] → t[i])不够,因为可能 s 中不同字符映射到同一个 t 字符(破坏单射)。必须同时检查:s[i] 是否已映射过、t[i] 是否已被别的 s 字符映射过。
- 声明两个
std::unordered_map<char char></char>:map_s_to_t和map_t_to_s - 遍历下标
i,对每对s[i]和t[i]:- 若
s[i]在map_s_to_t中存在,但值不等于t[i]→ 返回false - 若
t[i]在map_t_to_s中存在,但值不等于s[i]→ 返回false - 否则插入:
map_s_to_t[s[i]] = t[i],map_t_to_s[t[i]] = s[i]
- 若
长度不等直接返回 false;空串视为同构。
立即学习“C++免费学习笔记(深入)”;
用数组替代哈希表加速 ASCII 字符场景
如果明确限定输入为小写英文('a'–'z'),用两个大小为 26 的 int 数组更高效,避免哈希开销和内存分配:
int map_s[26] = {0}; // 初始化为 0,0 表示未映射
int map_t[26] = {0};
for (int i = 0; i < s.length(); ++i) {
int idx_s = s[i] - 'a';
int idx_t = t[i] - 'a';
if (map_s[idx_s] != 0 && map_s[idx_s] != idx_t + 1) return false;
if (map_t[idx_t] != 0 && map_t[idx_t] != idx_s + 1) return false;
map_s[idx_s] = idx_t + 1; // +1 避免 0 冲突
map_t[idx_t] = idx_s + 1;
}注意:+1 是为了区分“未映射(0)”和“映射到 'a'(即 0)”,否则无法判断是否已设值。
容易忽略的边界和陷阱
常见错误不是逻辑错,而是细节没兜住:
- 没检查
s.length() == t.length(),导致越界访问或漏判 - 用
std::map而非std::unordered_map,无必要地引入O(log n)开销 - 只维护单向映射(如只用
map_s_to_t),漏掉t中字符被复用的情况 - 初始化数组时没清零(尤其全局/静态数组),残留旧值干扰判断
- 把
char当作有符号类型处理,遇到非 ASCII 字符(如扩展 ASCII 或 UTF-8 单字节高位)导致负索引
实际项目里,如果输入不确定字符集范围,坚持用 std::unordered_map 更安全;只有确认是纯 a-z 且性能敏感时才换数组方案。


















