罗马数字转整数遵循「右减左加」规则:若s[i]值小于s[i+1],则用后者减前者并跳过下一位;否则直接累加;需用哈希表或switch映射字符值,且遍历时必须检查i+1边界。

罗马数字转整数的核心逻辑是「右减左加」
罗马数字不是简单查表相加,关键规则是:当小值字符出现在大值字符左侧时(如 IV、IX),它表示减法;否则为加法。所以不能从左到右无脑累加,得先看右边一位。
典型错误是写成「遇到 I 就减 1,遇到 V 就加 5」——这会把 IV 算成 6。正确做法是:遍历到位置 i 时,比较 s[i] 和 s[i+1] 对应的数值,若前者小于后者,就用后者减前者,并跳过下一位。
- 用
std::unordered_map<char int></char>存映射比switch更直观,但注意unordered_map查找有常数开销,对短字符串影响可忽略 - 必须检查
i + 1 ,否则访问 <code>s[i+1]会越界 -
std::string索引是size_t类型,和负数比较要小心;建议用有符号整型索引或显式转换
手写映射表比硬编码 switch 更易维护
虽然 switch 在编译期优化好,但 7 个字母的映射写 switch 显得啰嗦,且新增扩展(比如支持小写)不方便。用 std::map 或 unordered_map 更贴近实际工程习惯。
示例映射初始化:
立即学习“C++免费学习笔记(深入)”;
std::unordered_map<char, int> roman = {
{'I', 1}, {'V', 5}, {'X', 10},
{'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}
};
- 所有键必须是大写;若输入含小写,需提前用
std::toupper转换 -
unordered_map平均查找 O(1),map是 O(log n),但这里只有 7 个元素,差别几乎为零 - 别忘了包含头文件:
<unordered_map>、<cctype>(用于toupper)
边界情况:空串、非法字符、超长输入
题目通常保证输入合法,但真实场景中,空字符串、含空格、字母外字符(如 '@')或超长串(千万级)都可能触发未定义行为或性能问题。
- 空串直接返回 0,避免进入循环
- 遇到映射表里不存在的字符(如
roman.find(c) == roman.end()),应抛异常或返回错误码;不建议静默忽略 - 如果要求严格校验格式(如不允许
IIII),需额外做语法检查,但标准 LeetCode 题目不要求 - 整数溢出一般不用防——最大合法罗马数字是
MMMCMXCIX(3999),远低于int上限
完整可运行片段(含输入校验)
下面是一个最小可行实现,处理空串和非法字符,并用 size_t 安全索引:
#include <string>
#include <unordered_map>
#include <cctype>
<p>int romanToInt(const std::string& s) {
if (s.empty()) return 0;</p><pre class='brush:php;toolbar:false;'>std::unordered_map<char, int> roman = {
{'I', 1}, {'V', 5}, {'X', 10},
{'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}
};
int result = 0;
size_t i = 0;
while (i < s.length()) {
char c = std::toupper(s[i]);
if (roman.find(c) == roman.end()) {
throw std::invalid_argument("Invalid Roman character: " + std::string(1, c));
}
if (i + 1 < s.length()) {
char next = std::toupper(s[i + 1]);
if (roman.find(next) != roman.end() && roman[c] < roman[next]) {
result += roman[next] - roman[c];
i += 2;
continue;
}
}
result += roman[c];
++i;
}
return result;}
最易被忽略的是大小写混合输入和越界检查——很多人只测了大写样例,上线后遇到 "mcmxciv" 就崩了。


















