首页/新闻资讯/正文详情

C++实现LeetCode(205.同构字符串)

发布时间:2026/11/2 15:30:01 来源:云帆数科 栏目:资讯中心
C++实现LeetCode(205.同构字符串)
前言LeetCode 205 要求判断两个字符串是否同构(isomorphic)。定义是:字符串s中的每个字符都能被替换成t中对应位置的字符,替换必须保持一致,且不同字符不能映射到同一个字符;反过来,t中的不同字符也不能由s中的同一个字符映射而来。换句话说,两个字符串的字符之间要存在一个双射(bijection)。题目保证两个字符串长度相同,但工程实现里仍然值得自己再校验一次。这道题的经典错误是只建立单向映射。例如s badc、t baba,只检查s到t,会看到b→b、a→a、d→b、c→a,每个s字符都只有一个映射,检查通过;但t中的b同时来自s的b和d,t中的a同时来自a和c,这不满足双射。因此必须同时维护s→t与t→s两张映射表,任何一张出现冲突都返回假。另一个常见误解是把「同构」理解成「两个字符串的字符出现次数分布相同」。ab与aa的计数分布不同,确实不同构;但ab与cc的计数分布相同(都是 1 和 1),它们不同构,因为两个不同的s字符不能映射到同一个t字符。所以计数法在这里是错的,必须检查映射关系。本文给出三种实现:双哈希表、定长数组、首次出现位置比较;分析各自的时间与空间复杂度,并说明 ASCII 前提与 Unicode 输入下的限制。示例以 C17 为基准。一、双映射的判定逻辑设两个字符串长度为n。对每个位置i,记a s[i]、b t[i]。维护s2t与t2s两张表。若a尚未出现在s2t中,写入s2t[a] b;否则检查s2t[a] b,不等则冲突。对t2s做对称的处理。只有两张表在所有位置都不冲突,才返回真。为什么两张表缺一不可?单看s→t,约束的是「同一个s字符必须始终映射到同一个t字符」;单看t→s,约束的是「同一个t字符必须始终来自同一个s字符」。前者排除foo与bar(第二个o要求映射到a,与第一次的b冲突),后者排除badc与baba。两个方向合起来才是双射。n为 0 或 1 时,循环不执行或只执行一次,自然返回真,不需要特判。二、哈希表实现用std::unordered_mapchar, char存两张表,查找与插入的均摊复杂度是 O(1),总时间 O(n),空间 O(min(n, 字符集大小))。实现时要用find查询,绝不能用operator[]:后者在键不存在时会插入一个值初始化的条目,既污染表,又让「是否存在」无法判断。元素插入用emplace,它返回pairiterator, bool,第二个成员表示是否真的插入了。这里不需要这个返回值,直接调用即可。三、定长数组实现LeetCode 的输入限定为可打印 ASCII,字符范围有限,可以用两个长度 256 的数组代替哈希表。数组里存「映射到的字符」,并用一个额外的布尔数组或特殊值表示「尚未映射」。更简洁的做法是把数组初始化为 -1,表示未映射;映射后用字符的整数值填充。用数组时有一个必须注意的细节:char是否有符号由实现定义。若char是有符号的,取值为 128 到 255 的字节会被解释成负数,直接拿来当下标就是越界访问,属于未定义行为(undefined behavior, UB)。正确做法是先static_castunsigned char再转std::size_t。数组版的时间仍是 O(n),空间固定为常数,常数因子比哈希表小,适合字符集固定的场景。但它把「只支持单字节字符集」这个前提写死了,遇到 UTF-8 多字节字符就会出错,接口上应当注明。四、首次出现位置法还有一个很短的写法:对每个位置i,检查s.find(s[i])是否等于t.find(t[i])。原理是,两个字符串同构当且仅当「每个字符首次出现的位置」在两个串中一一对应。因为首次出现位置编码了字符的出现次序与重复模式,模式相同就是同构。这个写法代码最短,但std::string::find每次从头扫描,最坏情况下每个位置都要扫一遍,总复杂度 O(n²)。字符串很长时不如前两种方法。它适合作为快速验证或面试时口述的思路,不适合放进性能敏感的代码。五、完整示例下面的示例实现三种方法,并在main中覆盖题目示例与几个边界用例。// 适用于 C17#include array#include iostream#include string#include unordered_map// 方法一:双哈希表,支持任意字符集(以 char 为单位)bool isIsomorphicMap(const std::string s, const std::string t) {if (s.size() ! t.size()) {return false;}std::unordered_mapchar, char s2t;std::unordered_mapchar, char t2s;for (std::size_t i 0; i s.size(); i) {const char a s[i];const char b t[i];const auto it s2t.find(a);if (it s2t.end()) {s2t.emplace(a, b);} else if (it-second ! b) {return false;}const auto jt t2s.find(b);if (jt t2s.end()) {t2s.emplace(b, a);} else if (jt-second ! a) {return false;}}return true;}// 方法二:定长数组,仅适用于单字节字符集bool isIsomorphicAscii(const std::string s, const std::string t) {if (s.size() ! t.size()) {return false;}std::arrayint, 256 s2t{};std::arrayint, 256 t2s{};s2t.fill(-1);t2s.fill(-1);for (std::size_t i 0; i s.size(); i) {const auto a static_castunsigned char(s[i]);const auto b static_castunsigned char(t[i]);if (s2t[a] -1) {s2t[a] static_castint(b);} else if (s2t[a] ! static_castint(b)) {return false;}if (t2s[b] -1) {t2s[b] static_castint(a);} else if (t2s[b] ! static_castint(a)) {return false;}}return true;}// 方法三:首次出现位置比较,代码最短但最坏 O(n^2)bool isIsomorphicFind(const std::string s, const std::string t) {if (s.size() ! t.size()) {return false;}for (std::size_t i 0; i s.size(); i) {if (s.find(s[i]) ! t.find(t[i])) {return false;}}return true;}int main() {struct Case {std::string s;std::string t;bool expected;};const Case cases[] {{egg, add, true},{foo, bar, false},{paper, title, true},{badc, baba, false},{ab, aa, false},{, , true},{a, z, true},};for (const Case c : cases) {const bool r1 isIsomorphicMap(c.s, c.t);const bool r2 isIsomorphicAscii(c.s, c.t);const bool r3 isIsomorphicFind(c.s, c.t);std::cout c.s / c.t - std::boolalpha r1 r2 r3 (expected c.expected )\n;}return 0;}按标准语义推演:egg与add中e→a、g→d,两张表都一致,返回真;foo与bar中第一个o→a,第二个o→r冲突,返回假;paper与title的映射为p→t、a→i、e→l、r→e,双向一致;badc与baba在t2s上冲突;ab与aa在t2s上冲突。三份实现对这些用例应当给出一致结果。以上只按代码逻辑推导,未经编译运行核对。常见坑点一、只维护一个方向的映射❌ 只建s2t,把badc与baba判成同构。✅ 同时维护s2t与t2s,任一方向冲突都返回假。二、用operator[]查询映射表❌if (s2t[a] 0) s2t[a] b;——键不存在时operator[]会插入默认值,既改表又让「是否映射过」失真。✅ 用find比较end(),插入用emplace。三、忘记检查长度❌ 直接按s.size()循环并访问t[i],长度不等时越界,std::string::operator[]越界是 UB。✅ 入口处if (s.size() ! t.size()) return false;。四、用计数分布判断同构❌ 统计两个串的字符频次,频次分布相同就返回真,ab与cc会被误判。✅ 同构要求存在双射,必须检查映射关系,而不是频次。五、char下标未转无符号❌s2t[s[i]]在char有符号且字节值大于 127 时下标为负,越界访问。✅ 用static_castunsigned char(s[i])再作为下标;数组大小取 256。六、认为空串或单字符需要特判❌ 额外加if (s.empty()) return true;的分支,逻辑重复且容易与长度检查冲突。✅ 循环本身就能正确处理n 0与n 1,只保留长度相等检查即可。七、用find法处理超长字符串❌ 每个位置都从头find,最坏 O(n²),长输入下明显变慢。✅ 用双哈希表或定长数组,时间 O(n);find法只作演示。八、把方法二用于 UTF-8 文本❌ 认为按字节比较等价于按字符比较,多字节字符被拆成多个「字符」参与映射。✅ 方法二的前提是单字节字符集;处理 Unicode 时应先解码成码点序列,再用哈希表按码点比较。总结方法时间复杂度空间复杂度字符集前提适用场景双哈希表O(n) 均摊O(min(n, σ))无(以char为单位)通用实现定长数组O(n)O(256)单字节字符集ASCII、追求常数首次出现位置O(n²) 最坏O(1) 额外无演示、短字符串判断同构的关键不是「字符出现次数」,而是「两个方向上的映射是否都一致」。实现时把长度检查、双向映射、以及数组下标的无符号转换这三处写对,这道题就没有别的陷阱了。若输入可能包含多字节字符,先明确字符集与解码方式,再套用同样的双映射逻辑。

相关推荐

四色魔豆4种
四色魔豆4种

目录 四色魔豆1 复原方法 待更新 四色魔豆1 数字版 1,问题模型 2,复原方法 待更新 四色魔豆2 复原方法 待更新 四色魔豆2 数字版 1,问题模型 2,复原方法 待更新 四色魔豆1 复原方法 待更新 四色魔豆1 数字版 1&#x… · 2026/11/2 15:30:01

上海日式搬家怎么选?虎狮搬场实用避坑核验指南
上海日式搬家怎么选?虎狮搬场实用避坑核验指南

上海日式搬家怎么选?虎狮搬场实用避坑核验指南一、上海日式搬家常见踩坑痛点梳理最近身边好几个上海的朋友搬家都踩了日式搬家的坑。近几年本地日式搬家需求涨幅明显,不少非专业商家也混进市场,踩坑情况屡见不鲜。最常见的几类问题可以归纳如… · 2026/11/2 15:30:01

开源 AI 影视解说工具怎么选:8 个项目对比,附一套试跑核对法(2026)
开源 AI 影视解说工具怎么选:8 个项目对比,附一套试跑核对法(2026)

想把一段视频自动做成中文解说,开源工具按用法分四类:本地网页界面、桌面应用、命令行,以及装进编程 Agent 的 skill。分好类,再核对四件事:工具靠什么看懂视频,模型和配音调用谁、怎么付费,能不… · 2026/11/2 15:30:01

TIFF LZW解码报错排查:tifffile缺imagecodecs与降级读取
TIFF LZW解码报错排查:tifffile缺imagecodecs与降级读取

这个报错我见过太多次了。第一次撞上的时候,我盯着 TiffPage 0 这半句看了好一会儿才反应过来——页号 0,意思是连第一页都没读进去,后面那几百行预处理代码一行都没机会跑。整行提示其实只说了一件事:这个 TIFF 用的是 LZW 压缩… · 2026/11/3 11:47:26

ZooKeeper节点操作实战:类型选型、版本控制与异常排查
ZooKeeper节点操作实战:类型选型、版本控制与异常排查

节点操作这四个字听着像入门第一章,实际工作中出问题最多的偏偏就是这一层。create、get、set、delete 四条命令,看着比数据库的增删改查还简单,可我在几个项目里处理过的问题,追到根上全是节点没设计好:临时节点挂在持… · 2026/11/3 11:47:26

智能宠物护理市场与海外网红营销策略
智能宠物护理市场与海外网红营销策略

1. 孤独经济下的宠物护理市场新机遇最近两年,一个有趣的现象正在全球范围内蔓延:越来越多的年轻人开始养宠物,但却因为工作繁忙或经常出差,不得不将宠物独自留在家中。这种现象催生了一个新兴市场——空巢宠物智能护理。作为长期关… · 2026/11/3 11:47:26

Nginx反向代理Tomcat多项目路径转发配置与避坑
Nginx反向代理Tomcat多项目路径转发配置与避坑

1. 把 Nginx 摆在 Tomcat 前面,到底图什么 我第一次接触 Nginx 反向代理 Tomcat 多项目这套组合,是因为一个后台系统越堆越臃肿:前端页面、接口服务、文件下载、定时任务的管理页面全塞在一个 Tomcat 的 webapps 目录下,端口 8080… · 2026/11/3 11:47:26

Parasoft自动化测试工具核心价值与实战应用
Parasoft自动化测试工具核心价值与实战应用

1. Parasoft自动化测试工具的核心价值解析Parasoft作为业界领先的自动化测试解决方案提供商,其工具链覆盖了单元测试、API测试、UI测试等全栈测试场景。不同于普通测试工具,Parasoft的核心优势在于其"质量门禁"理念——通过静态代码分析、动态… · 2026/11/3 11:47:26

MySQL 5.7全量与增量备份最佳实践
MySQL 5.7全量与增量备份最佳实践

1. MySQL备份方案设计背景数据库备份是DBA日常工作中最基础也最重要的任务之一。我在金融行业做MySQL DBA的8年时间里,见过太多因为备份方案不完善导致的数据灾难案例。有一次,某电商平台在"双11"大促期间主库宕机,由于增量备份间隔… · 2026/11/3 11:46:25

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码