LeetCode 第12题:整数转罗马数字(Python3解法)

1:问题描述

来源:LeetCode

难度:中等


问题详情:
罗马数字包含以下七种字符: IVXLCDM

字符 数值
I I I 1
V V V 5
X X X 10
L L L 50
C C C 100
D D D 500
M M M 1000

例如, 罗马数字 2 写做 II ,即为两个并列的 112 写做 XII ,即为 X + II27 写做 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 = 1994
输出: “MCMXCIV”
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示

1 < = n u m < = 3999 1 <= num <= 3999 1<=num<=3999


2:问题分析

2.1 时间复杂度和空间复杂度

在真正开始介绍各种算法前,先以表格形式展示各自的时间复杂度和空间复杂度,其中 n n n表示字符串 s s s的长度。

算法 时间复杂度 空间复杂度
模拟 O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1)
硬编码 O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1)
自己的解法 O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1)

2.2 模拟

图片来源
在这里插入图片描述
我们先来看一下示例中的1994是如何转换成罗马数字的。
首先数字1994被拆解成1000+900+90+4.
可以看出每一位都是用选取尽可能大的数值。

2.2.1 代码

构建一个从大到小的元组对,遍历每一个元组对,如果当前num值大于等于当前元组中的数值key,那么就减去足够多的key,使 n u m < k e y num<key num<key,同时减了多少次key,就要记录多少次key对应的罗马字符。
直至 n u m = 0 num=0 num=0,循环结束。

def intToRoman3(num: int) -> str:
    """对方法2中相减那一处的优化"""
    VALUE_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"),
    ]
    result = ''
    for key, value in VALUE_SYMBOLS:
        if num >= key:
            count = (num // key)
            num -= count * key
            result += count * value
        if num == 0:
            break

    return result

时间复杂度为 O ( 1 ) O(1) O(1),空间复杂度为 O ( 1 ) O(1) O(1)

2.3 硬解码

上述方案是从上图中的每一个“挡位”对应的罗马字符出发构建的。

而硬解码的方案则是从num数值中每一位的角度出发。
由提示可以得到,num值最多为3999;所以我们先从千位出发,考虑其能够取到的值,是1、2、3,然后构建其对应的罗马数字’M‘,'MM','MM';
同理,我们构建了如下表格(表格来源):
在这里插入图片描述

2.3.1 代码

代码如下:

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 intToRoman4(num: int) -> str:
    """硬编码,就是把每一位的所有可能都列出来,以空间换时间"""
    return THOUSANDS[num // 1000] + HUNDREDS[num % 1000 // 100] + TENS[num % 100 // 10] + ONES[num % 10

时间复杂度为 O ( 1 ) O(1) O(1),空间复杂度为 O ( 1 ) O(1) O(1)

2.3 我的解法

我的解法同样从每一位出发,但是不使用硬编码的形式,而是通过当前位数的数值分析其对应的罗马数字。

比如百位中如果是9或者4,则对应的是'CM'’CD‘
如果是7,则大于等于5的部分使用一个’D‘表示,不超过的部分即(7-5=2)使用’C‘×2表示;
如果百位本身就是小于4的数,比如3,则直接使用’C‘×3表示;

2.3.1 代码

代码中构建了一个二维列表,每个元素中都存储了当前位数的两个对应的罗马字符。
比如百位,第0个元素是’D‘,第1个元素是’C‘。

def intToRoman(num: int) -> str:
    special = {
    
    9: 'IX', 4: 'IV', 40: 'XL', 90: 'XC', 400: 'CD', 900: 'CM'}
    index_str = [['V', 'I'], ['L', 'X'], ['D', 'C'], ['', 'M']]
    index = [3, 2, 1, 0]
    result = ''
    for i in index:
        cur, num = divmod(num, 10 ** i)
        if cur == 4 or cur == 9:
            result += special.get(cur * 10 ** i, '')
        elif 9 > cur >= 5:
            result += index_str[i][0]
            result += (cur - 5) * index_str[i][1]
        elif cur < 4:
            result += cur * index_str[i][1]
    return result

时间复杂度为 O ( 1 ) O(1) O(1),空间复杂度为 O ( 1 ) O(1) O(1)

猜你喜欢

转载自blog.csdn.net/weixin_43490422/article/details/126735396
今日推荐