1. 题目解析与核心概念
同构字符串是LeetCode中一道经典的字符串处理题目(编号205),要求判断两个字符串是否满足"同构"关系。所谓同构字符串,指的是可以通过字符的一一映射关系将一个字符串完全转换为另一个字符串。例如"egg"和"add"就是同构的(e→a,g→d),而"foo"和"bar"则不是(o需要同时映射到a和r)。
这道题的核心在于理解"双射"(bijection)的概念——即两个字符串之间的字符映射必须满足:
- 单射:s中的同一个字符不能映射到t的不同字符
- 满射:t中的字符必须都有对应的s字符
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. C语言实现基础版
2.1 哈希表法实现思路
最直观的解法是使用两个哈希表(在C中可以用数组模拟)分别记录s→t和t→s的映射关系:
c复制#define ASCII_SIZE 128
bool isIsomorphic(char* s, char* t) {
char map_s[ASCII_SIZE] = {0};
char map_t[ASCII_SIZE] = {0};
for(int i = 0; s[i]; i++) {
// 检查s→t映射是否一致
if(map_s[s[i]] == 0) {
map_s[s[i]] = t[i];
} else if(map_s[s[i]] != t[i]) {
return false;
}
// 检查t→s映射是否一致
if(map_t[t[i]] == 0) {
map_t[t[i]] = s[i];
} else if(map_t[t[i]] != s[i]) {
return false;
}
}
return true;
}
2.2 关键点解析
- ASCII_SIZE设为128是为了覆盖所有标准ASCII字符
- 两个映射表必须同时检查,确保双射关系
- 时间复杂度O(n),空间复杂度O(1)(因为数组大小固定)
注意:LeetCode的测试用例中字符串可能包含非字母字符,所以不能假设输入仅为小写字母
3. 优化解法与变种
3.1 单哈希表实现
可以通过记录字符首次出现的位置来判断映射关系:
c复制bool isIsomorphic(char* s, char* t) {
int map_s[256] = {0}, map_t[256] = {0};
int n = strlen(s);
for(int i = 0; i < n; i++) {
if(map_s[s[i]] != map_t[t[i]])
return false;
map_s[s[i]] = i + 1; // +1避免与初始值0冲突
map_t[t[i]] = i + 1;
}
return true;
}
3.2 性能对比
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 双哈希表法 | O(n) | O(1) | 通用解法 |
| 位置记录法 | O(n) | O(1) | 字符串较长时更优 |
4. 常见错误与调试技巧
4.1 典型错误案例
-
只检查单向映射:
c复制// 错误示例:缺少t→s的检查 if(map_s[s[i]] && map_s[s[i]] != t[i]) return false; -
数组越界:
c复制char map[26] = {0}; // 错误:无法处理非字母字符
4.2 调试建议
- 打印中间映射表:
c复制printf("s[%c]->t[%c]\n", s[i], t[i]); - 边界测试用例:
- 空字符串
- 长度不等的字符串
- 包含数字/特殊字符的字符串
5. 算法扩展与应用
5.1 相似题目
- LeetCode 290. 单词规律(本质相同的双射问题)
- LeetCode 242. 有效的字母异位词(单射问题)
5.2 实际应用场景
- 密码学中的替换密码验证
- 生物信息学中的序列比对
- 编译器设计中的符号表管理
6. 工程实践建议
-
防御性编程:
c复制if(!s || !t) return false; if(strlen(s) != strlen(t)) return false; -
性能优化:
- 提前计算字符串长度避免重复调用strlen
- 使用位运算替代数组访问(在特定场景下)
-
可读性改进:
c复制#define MAPPING_EXIST(map, key, val) \ ((map[key] != 0) && (map[key] != val))
在实际面试中,建议先阐述双射概念,再逐步实现基础解法,最后讨论优化空间。这道题虽然简单,但能很好考察对哈希表和字符串操作的理解深度。
