罗马数字转整数
在 LeetCode 上查看 ↗语音讲解
开车或通勤时可听,跟着思路走一遍
速度
题目描述
罗马数字包含以下七种字符:I、V、X、L、C、D 和 M。
| 字符 | 数值 |
|---|---|
| I | 1 |
| V | 5 |
| X | 10 |
| L | 50 |
| C | 100 |
| D | 500 |
| M | 1000 |
通常情况下,小的数字在大的数字右边;但若小的数字在大的数字左边,则表示减去该值(如 IV=4,IX=9)。该减法规则仅适用于六种情况:I 在 V/X 前、X 在 L/C 前、C 在 D/M 前。
给定一个罗马数字,将其转换成整数。
示例 1
输入:s = "III"
输出:3
示例 2
输入:s = "IV"
输出:4
示例 3
输入:s = "IX"
输出:9
示例 4
输入:s = "LVIII"
输出:58
L = 50,V = 5,III = 3。
示例 5
输入:s = "MCMXCIV"
输出:1994
M = 1000,CM = 900,XC = 90,IV = 4。
模拟答题者思考
1. 最直接:把每个字符的值查表直接相加——IV 会变成 1+5=6,显然错了。
2. 找重复:减法形式都是「小字符在大字符左边」,如 I 在 V 前表示 5-1=4。只需处理这 6 种特例?可以,但要写一堆 if s[i:i+2] in ...,冗长且难维护。
3. 统一规则:从左到右扫,若 roman[s[i]] < roman[s[i+1]],说明当前位被「借走」做减法,ans -= roman[s[i]];否则正常累加。这样 IV、IX、CM 等全部自动处理。
4. 另一种等价写法是从右往左扫:若当前值 < 已处理的右边值就减,否则加——思路相同,选一种写顺手的即可。
5. 复杂度:字符串最长 15,一次线性扫描 O(n),哈希表 O(1) 空间,足够高效。
变量语义(先读这三句再编码)
| 变量 | 类型 | 语义(三句法) |
|---|---|---|
roman | dict | 定义:字符到数值的映射表(I→1, V→5, …, M→1000) 维护:固定不变,覆盖全部 7 种符号 更新:无需更新,查询 roman[s[i]] 即可 |
ans | int | 定义:从左到右扫描后累计的整数值 维护:每处理一个字符,按「加或减」规则更新 更新:若当前字符值 < 下一字符值则 ans -= val,否则 ans += val |
i | int | 定义:当前扫描到的字符下标 维护:从 0 遍历到 len(s)-1,每次右移一位更新: i++;判断减法时需偷看 s[i+1] |
落码步骤
1. 建立 roman 字符→数值映射表
2. 初始化 ans = 0,从左到右遍历下标 i
3. 取 val = roman[s[i]];若 i+1 < len(s) 且 val < roman[s[i+1]],则 ans -= val,否则 ans += val
4. 遍历结束返回 ans
代码实现
class Solution:
def romanToInt(self, s: str) -> int:
roman = {"I": 1, "V": 5, "X": 10, "L": 50,
"C": 100, "D": 500, "M": 1000}
ans = 0
for i in range(len(s)):
val = roman[s[i]]
# 当前位比右边小 → 减法形式(如 I 在 V 前)
if i + 1 < len(s) and val < roman[s[i + 1]]:
ans -= val
else:
ans += val
return ans
class Solution {
public:
int romanToInt(string s) {
unordered_map<char, int> roman = {
{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50},
{'C', 100}, {'D', 500}, {'M', 1000}
};
int ans = 0;
for (int i = 0; i < (int)s.size(); ++i) {
int val = roman[s[i]];
if (i + 1 < (int)s.size() && val < roman[s[i + 1]]) {
ans -= val;
} else {
ans += val;
}
}
return ans;
}
};
// 时间 O(n),空间 O(1)
复杂度分析
时间复杂度
O(n)
空间复杂度
O(1)
常见坑
不能对所有字符直接求和,否则 IV 会得到 6 而非 4。
判断减法时要比较数值而非字符 ASCII(虽然本题数据下碰巧一致,但语义上应查表比较)。
遍历时注意边界:最后一位没有「下一字符」,永远做加法。
必测边界 Case
Case 1:纯加法
s = "III" → 3
Case 2:单位减法
s = "IV" → 4,s = "IX" → 9
Case 3:复合串
s = "MCMXCIV" → 1994(同时含 CM、XC、IV 三种减法形式)