题目难度:中等
默认优化目标:最小化平均时间复杂度。
Python默认为Python3。
目录
1 题目描述
七个不同的符号代表罗马数字,其值如下:
符号 | 值 |
---|---|
I | 1 |
V | 5 |
X | 10 |
L | 50 |
C | 100 |
D | 500 |
M | 1000 |
罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:
-
如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
-
如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (
V
) 减 1 (I
):IV
,9 是 10 (X
) 减 1 (I
):IX
。仅使用以下减法形式:4 (IV
),9 (IX
),40 (XL
),90 (XC
),400 (CD
) 和 900 (CM
)。 -
只有 10 的次方(
I
,X
,C
,M
)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V
),50 (L
) 或 500 (D
)。如果需要将符号附加4次,请使用 减法形式。
给定一个整数,将其转换为罗马数字。
示例 1:
输入:num = 3749
输出: "MMMDCCXLIX"
解释:
3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M) 700 = DCC 由于 500 (D) + 100 (C) + 100 (C) 40 = XL 由于 50 (L) 减 10 (X) 9 = IX 由于 10 (X) 减 1 (I) 注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位
示例 2:
输入:num = 58
输出:"LVIII"
解释:
50 = L 8 = VIII
示例 3:
输入:num = 1994
输出:"MCMXCIV"
解释:
1000 = M 900 = CM 90 = XC 4 = IV
提示:
-
1 <= num <= 3999
2 题目解析
与LeetCode面试150——13罗马数字转整数不同,这题的输入是阿拉伯数字,输出是罗马字符。阿拉伯数字和罗马字符的对应关系如下:
符号 | 值 |
---|---|
I | 1 |
V | 5 |
X | 10 |
L | 50 |
C | 100 |
D | 500 |
M | 1000 |
有几种特殊情况,如下:
字符 | 数值 |
---|---|
IV | 4 |
IX | 9 |
XL | 40 |
XC | 90 |
CD | 400 |
CM | 900 |
3 算法原理及代码实现
3.1 模拟
每一个阿拉伯数字都有唯一一个对应的罗马字符。所以我们需要从大到小一个一个去匹配,因为罗马数字的规则是:对于罗马数字从左到右的每一位,选择尽可能大的符号值。
举一个例子,数字160。160<M,不行。160<CM,不行。160<D,不行。160<CD,不行。160>C,余下60。60<XC,不行。60>L,余下10。10<XL,不行。10=X,余下零,结束。因此
我们模拟这个过程,首先建立一个列表存储所有罗马字符和阿拉伯数字的对应关系,然后从大到小依次遍历。
平均时间复杂度为O(1),平均空间复杂度为O(1)。
C++代码实现
const pair<int,string> valueToSymbols[]={
{1000,"M"},
{900,"CM"},
{500,"D"},
{400,"CD"},
{100,"C"},
{90,"XC"},
{50,"L"},
{40,"XL"},
{10,"X"},
{9,"IX"},
{5,"V"},
{4,"IV"},
{1,"I"},
};
class Solution{
public:
string intToRoman(int num){
string roman;
for(const auto &[value,symbol] : valueToSymbols){
while(num>=value){
num-=value;
roman+=symbol;
}
if(!num){
break;
}
}
return roman;
}
};
Python代码实现
class Solution:
def intToRoman(self, num: int) -> str:
value_to_symbols = [
(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
(100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
(10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")
]
roman = []
for value, symbol in value_to_symbols:
while num >= value:
num -= value
roman.append(symbol)
if num == 0:
break
return ''.join(roman)
3.2 硬编码数字
我们观察前面的罗马字符和阿拉伯数字对应关系表,我们发现:
· 千位只能用M表示
· 百位能用C、CD、D与CM表示
· 十位能用X、XL、L与XC表示
· 个位能用I、IV、V和IX表示
我们能得到如下的硬编码表
千位 | 百位 | 十位 | 个位 | |
---|---|---|---|---|
0 | ||||
1 | M | C | X | I |
2 | MM | CC | XX | II |
3 | MMM | CCC | XXX | III |
4 | 4*M | CD | XL | IV |
5 | 5*M | D | L | V |
6 | 6*M | DC | LX | VI |
7 | 7*M | DCC | LXX | VII |
8 | 8*M | DCCC | LXXX | VIII |
9 | 9*M | CM | XC | IX |
由于题目输入的范围为1 <= num <= 3999
,因此千位最多取到MMM。
我们可以利用模运算和除法运算,得到num每个位上的数字
平均时间复杂度为O(1),平均空间复杂度为O(1)。
C++代码实现
const string thousands[] = {"", "M", "MM", "MMM"};
const string hundreds[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
const string tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
const string ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
class Solution {
public:
string intToRoman(int num) {
return thousands[num / 1000] + hundreds[num % 1000 / 100] + tens[num % 100 / 10] + ones[num % 10];
}
};
Python代码实现
class Solution:
thousands = ["", "M", "MM", "MMM"]
hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]
tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]
ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]
def intToRoman(self, num: int) -> str:
return Solution.thousands[num//1000]+Solution.hundreds[num%1000//100]+Solution.tens[num%100//10]+Solution.ones[num%10]