LeetCode(12&13)—— 整数与罗马数字相互转化

说明

如果你在写这个程序的时候,发现你在IDE上运行结果完全正确,而在网站上总是Wrong Answer,恭喜你,这将让你倍涨经验(起码我找了很久才发现这个问题)

字典是存放顺序与你输入的顺序是不一样的!!!迭代读取的时候一定要小心!!!!!

下面讲下经过。。。

描述

罗马数字包含以下七种字符: 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 到 3999 的范围内。

示例1:
输入: 3
输出: "III"
示例2:
输入: 4
输出: "IV"
示例3:
输入: 9
输出: "IX"
示例4:
输入: 58
输出: "LVIII"
解释: C = 100, L = 50, XXX = 30, III = 3.
示例5:
输入: 58
输出: "LVIII"
解释: C = 100, L = 50, XXX = 30, III = 3.

思路

乍一看,什么放左边放右边,有点凌乱的感jio,我们不妨把所有罗马数字的“基数”都给它罗列出来(当然是用字典了),一共有以下这些“基数”。
这里的字典一定要按照基数值从大到小的顺序排列 ,就好比我们从高位到低位写一个数字,总是从高位写到低位。

        dict_roma = {
            3000: 'MMM',
            2000: 'MM',
            1000: 'M',
            900: 'CM',
            500: 'D',
            400: 'CD',
            300: 'CCC',
            200: 'CC',
            100: 'C',
            90: 'XC',
            50: 'L',
            40: 'IL',
            30: 'XXX',
            20: 'XX',
            10: 'X',
            9: 'IX',
            5: 'V',
            4: 'IV',
            3: 'III',
            2: 'II',
            1: 'I',
        }

然后再逐个去除基数,看有没有商(够不够除),一旦够除,商肯定也只能是1,这里用到了defaultdict(int)这个标准库中的字典,方便统计数量。

最后再根据统计的情况生成最终结果字符串即可。

Code

from collections import defaultdict


class Solution:
    def intToRoman(self, num):
        """
        :type num: int
        :rtype: str
        """
        # 这里的预定义字典一定要从数值大道数值小排列
        dict_roma = {
            3000: 'MMM',
            2000: 'MM',
            1000: 'M',
            900: 'CM',
            500: 'D',
            400: 'CD',
            300: 'CCC',
            200: 'CC',
            100: 'C',
            90: 'XC',
            50: 'L',
            40: 'IL',
            30: 'XXX',
            20: 'XX',
            10: 'X',
            9: 'IX',
            5: 'V',
            4: 'IV',
            3: 'III',
            2: 'II',
            1: 'I',
        }

        num_dict = defaultdict(int)
        for key in dict_roma.keys():
            if int(num / key) != 0:
                num_dict[key] += 1
                num -= key
            # 查找完了,提前退出,后面的key无需再试探
            if num == 0:
                break
        result = ""
        for key, value in num_dict.items():
            if value != 0:
                result += dict_roma[key]

        return result

s = Solution()
print(s.intToRoman(4))

疑问

我在pycharm中运行程序,完全没问题,输入4时,输出为IV
这里写图片描述

可是在LeetCode跑起来,就是说我输入4的时候输出错误
这里写图片描述

我也是醉了,不知道是哪里的问题,真的想了很久很久。于是乎,我又遇到了
罗马数字转整数这个题,也出现了类似的错误,百度了一下,并没有找到什么相关答案。我灵机一动,是不是和字典的“无序性”有关?呵呵。。果然是这样

修改

上面说道,我用了defaultdict这个工具,实验证明,它也是无序的。于是我干脆自己统计好了(省去defaultdict提供的便利),用了两个OrderedDict(),关于OrderedDict()大家自行百度,我这里说明一下它的一个特别之处。

一般字典取value,我们肯定是dictionary[key]这样,但是在OrderedDict()中,dictionary[key]返回的是一个元组,所以取出具体值需要这样操作——
dictionary[key][0],ok,代码如下:

from collections import defaultdict
from collections import OrderedDict


class Solution:
    def intToRoman(self, num):
        """
        :type num: int
        :rtype: str
        """
        # 这里的预定义字典一定要从数值大道数值小排列
        dict_roma = OrderedDict()
        dict_roma['3000'] = 'MMM',
        dict_roma['2000'] = 'MM',
        dict_roma['1000'] = 'M',
        dict_roma['900'] = 'CM',
        dict_roma['500'] = 'D',
        dict_roma['400'] = 'CD',
        dict_roma['300'] = 'CCC',
        dict_roma['200'] = 'CC',
        dict_roma['100'] = 'C',
        dict_roma['90'] = 'XC',
        dict_roma['50'] = 'L',
        dict_roma['40'] = 'XL',
        dict_roma['30'] = 'XXX',
        dict_roma['20'] = 'XX',
        dict_roma['10'] = 'X',
        dict_roma['9'] = 'IX',
        dict_roma['5'] = 'V',
        dict_roma['4'] = 'IV',
        dict_roma['3'] = 'III',
        dict_roma['2'] = 'II',
        dict_roma['1'] = 'I'

        num_dict = OrderedDict()
        for key in dict_roma.keys():
            if int(num / int(key)) != 0:
                # 如果计数字典中存在这个键
                if key in num_dict:
                    num_dict[key] += 1
                else:
                    num_dict[key] = 1
                num -= int(key)
            # 查找完了,提前退出,a后面的key无需再试探
            if num == 0:
                break
        result = ""
        for key, value in num_dict.items():
            if value != 0:
                result += dict_roma[key][0]

        return result

另:罗马数字转整数,思路几乎一样,就是预定义字典的顺序改变一下。

from collections import OrderedDict


class Solution:
    def romanToInt(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 这里的预定义字典一定要从数值大道数值小排列
        dict_roma = OrderedDict()
        dict_roma['CM'] = 900,
        dict_roma['CD'] = 400,
        dict_roma['XC'] = 90,
        dict_roma['XL'] = 40,
        dict_roma['IX'] = 9,
        dict_roma['IV'] = 4,
        dict_roma['MMM'] = 3000,
        dict_roma['MM'] = 2000,
        dict_roma['M'] = 1000,
        dict_roma['D'] = 500,
        dict_roma['CCC'] = 300,
        dict_roma['CC'] = 200,
        dict_roma['C'] = 100,
        dict_roma['L'] = 50,
        dict_roma['XXX'] = 30,
        dict_roma['XX'] = 20,
        dict_roma['X'] = 10,
        dict_roma['V'] = 5,
        dict_roma['III'] = 3,
        dict_roma['II'] = 2,
        dict_roma['I'] = 1,

        base_list = list(dict_roma.keys())
        result = 0
        for item in base_list:
            try:
                base_index = s.index(item)
                num = dict_roma[item][0]
                result += num
                # 如果从头匹配到
                if base_index == 0:
                    s = s[len(item) + base_index:]
                # 如果在中间匹配到
                else:
                    s = s[:base_index] + s[len(item) + base_index:]
            except ValueError as e:
                continue
        return result

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值