LeetCode题目笔记--12.整数转罗马数字

23 篇文章 0 订阅
15 篇文章 0 订阅

题目描述

  题目跟前面13题描述一样,就是问题变为整数转成罗马数字。

思路

  上一道题罗马数字转整数比较简单,因为不存在罗马数字表示冲突的问题,即不存在一个罗马数字对应多个整数。而这个问题中,就要考虑一下这个问题了,因为如果不加以约束的话,一个整数可以用多种罗马数字来表示。比如对于2000:

2000 = 1000 + 1000 = M + M,即MM
2000 = 1000 + 900 + 100 = M + CM + C,即MCMC

  于是,我特地查了一下,罗马数字的排列规则是从左到右尽可能用最大的数字来表示。那么,就像之前那样,准备好一个字典,反过来以整数为键,罗马数字为值,存放13种键-值组合。
  然后,只要整数num(待转换的数)不为0,就在每次循环里找到一个离他最近的罗马数字对应的整数n,记录该罗马数字,然后让num-n,继续循环,直到num=0为止。
伪代码描述:

buf = {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’}
res = “”
while num != 0
  找到最接近num的整数n
  res += buf[n]
  num -= n
return res

python代码(low版)

class Solution:
    def intToRoman(self, num: int) -> str:
        buf = {1:'I', 5:'V', 10:'X', 50:'L', 100:'C', 
                500:'D', 1000:'M', 4:'IV', 9:'IX',
                40:'XL', 90:'XC', 400:'CD', 900:'CM'}
        d_keys = list(buf.keys())
        d_keys.sort(reverse = True)
        if num in d_keys:
            return buf[num]
        res = ''
        t = 0
        while num != 0:
            for n in d_keys:
                t = num - n
                if t >= 0:
                    res += buf[n]
                    num -= n
                    break

        return res

  这是第一版代码,运行时间80ms,慢得一*,观察代码,可以看见真的憨憨,创建字典时竟然乱序,后面还要排序,而且在while里每次for都是从第一个元素开始,这无疑浪费时间,从上次break的位置接着往后找他不香吗?而且再仔细想想,也没必要先判断num是否是正好对应现成的,反正in操作的原理也是循环,所以这里可以删掉。

改进一点的python代码

class Solution:
    def intToRoman(self, num: int) -> str:
        buf = {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'}
        d_keys = list(buf.keys())
        res = ''
        idx = 0
        size = d_keys.__len__()
        while num != 0:
            for i in range(idx, size):
                t = num - d_keys[i]
                if t >= 0:
                    res += buf[d_keys[i]]
                    num -= d_keys[i]
                    break
            idx = i

        return res

提交,运行时间64ms,还是有点慢,无奈去看了看官方题解,原来我前面的思路是贪心算法的思路,这我还真没想到。

最终代码

  官方题解的代码给了我灵感,原来可以这样写:

class Solution:
    def intToRoman(self, num: int) -> str:
        buf = {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'}
        res = ""
        for n in buf.keys():
            if num == 0:
                break

            quotient, num = divmod(num, n)
            res += buf[n] * quotient
        return res

  这样写的好处就在于一遍循环就能解决问题,那58来说,前面的代码在执行到num = 3时,会做3次for循环,而这3次循环都都几乎是同样的操作,效率极低,而这里用到的字符串“乘法”操作,在这种情况下就极大的提高效率,真香!

C++实现

class Solution {
public:
    string intToRoman(int num) {
        string res;
        /*map <int, string, greater<int>> dict = {{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"}};
        for(auto &t : dict)
        {
            if(num == 0)
            {
                break;
            }
            while(num >= t.first)
            {
                num -= t.first;
                res += dict[t.first];
            }
        }*/
        //更快的解法
        int key[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        string values[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        for(int i = 0; i < 13; i++)
        {
            if(num == 0)
            {
                break;
            }
            while(num >= key[i])
            {
                num -= key[i];
                res += values[i];
            }
        }
        return res;
    }
};

  注释掉的地方是用的map,运行时间32ms,只比python快了20ms,也许是创建map比较耗时的原因吧,我觉得还能更快,所以把键值对拆开成两个数组,这样下来运行时间是12ms。
  还有就是,就算给map的初值是按顺序写的,但map内部实现还是无序,因此还要在map的类型说明里加上greater参数,指明按int(这里就是键)降序排序。我第一次没加这个参数,结果答案全是“IIIIIIII…”,看了其他大佬的博客才明白,学到了学到了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值