力扣每日一题:12. 整数转罗马数字

题目: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];
        
    }
    
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暗夜无风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值