题目:12. 整数转罗马数字
难度: 中等
题目:
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
- I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
- X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
- C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给你一个整数,将其转为罗马数字。
示例1
输入: num = 3
输出: “III”
示例2
输入: num = 4
输出: “IV”
示例3
输入: num = 9
输出: “IX”
示例4
输入: num = 58
输出: “LVIII”
解释: L = 50, V = 5, III = 3.
示例5
输入: num = 1994
输出: “MCMXCIV”
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
- 1 <= num <= 3999
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/integer-to-roman/
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
注意到罗马数字只包含7种字符,可以采取一种数据结构将其存储,方便后面查找。注意到4和9两个位置的不同,也可以将类似的4、9、40、90等存储。
解题代码
(1)朴素模拟法
一开始的思路,首先,将num分解为从高到低的n位数字。如,58分为5和8,因为5其实是50,所以定义t记录当前的倍数。对于每一位,查表求值加入str。
class Solution {
private:
unordered_map<int, string> table;
public:
string intToRoman(int num) {
string str;
int t;
if (num >= 1000)
{
t = 1000;
}
else if (num >= 100)
{
t = 100;
}
else if (num >= 10)
{
t = 10;
}
else
{
t = 1;
}
table[1] = "I";
table[5] = "V";
table[10] = "X";
table[50] = "L";
table[100] = "C";
table[500] = "D";
table[1000] = "M";
while (num)
{
int tmp = num / t;
num = num - tmp * t;
str += getStr(tmp, t);
t /= 10;
}
return str;
}
string getStr(int tmp, int t)
{
string s;
if (tmp == 4 || tmp == 9)
{
s += table[t];
s += table[(tmp + 1) * t];
}
else if (tmp >= 5)
{
int num = tmp - 5;
s += table[5 * t];
for (int i = 0; i < num; i++)
{
s += table[t];
}
}
else
{
for (int i = 0; i < tmp; i++)
{
s += table[t];
}
}
return s;
}
};
(2)贪心模拟法
其实之前的思路可以优化,因为题目规定的num<=3999,所以我们只需要每次从最高的罗马字母表中判断最大值,满足num减去value,反之遍历表中下一个。利用贪心的思想,从最大数开始找,一直往后找。当然,用这种思路,我们需要将特殊情况(如:4、9)也用查找表存储起来并按降序存储。
class Solution {
public:
string intToRoman(int num) {
//按从大到小方式存储
pair<int, string> table[] = {
{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"},
};
string ans;
for (auto &[value, symbol] : table)
{
while (num >= value)
{
num -= value;
ans += symbol;
}
if (num == 0)
{
break;
}
}
return ans;
}
};
(3)编码
根据法1的思路,是通过每一位数字(0~9)来判断对应的罗马数字,使用t表示精度,而后通过相应的算法求出对应的值。而实际上,既然输入num<=3999,上限已知,那么我们直接将个、十、百、千对应的值用数组记录下来,然后每次对应位数查表,就能解决问题了。
class Solution {
public:
string intToRoman(int num) {
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 bits[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
return thousands[num / 1000] + hundreds[num % 1000 / 100] + tens[num % 100 / 10] + bits[num % 10];
}
};