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 到 3999 的范围内。

示例 4:
输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.

示例 5:
输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.

解法一

没看答案之前的做法,暴力求解法。
主要思路:从个位开始,遍历出每一位,然后根据各种情况再表示出来,并拼接到返回的字符串上。

    # ['I', 'V', 'X', 'L', 'C', 'D', 'M']
    # [1,    5,   10,  50,  100, 500, 1000]

    def handleNum(cur, ns):
        if cur < 4:
            ns = 'I' * cur + ns
        elif cur == 4:
            ns = 'IV' + ns
        elif cur == 5:
            ns = 'V' + ns
        elif cur < 9:
            ns = 'V' + 'I' * (cur - 5) + ns
        elif cur == 9:
            ns = 'IX' + ns
        elif cur < 40:
            ns = 'X' * (cur // 10) + ns
        elif cur == 40:
            ns = 'XL' + ns
        elif cur == 50:
            ns = 'L' + ns
        elif cur < 90:
            ns = 'L' + 'X' * (cur // 10 - 5) + ns
        elif cur == 90:
            ns = 'XC' + ns
        elif cur < 400:
            ns = 'C' * (cur // 100) + ns
        elif cur == 400:
            ns = 'CD' + ns
        elif cur == 500:
            ns = 'D' + ns
        elif cur < 900:
            ns = 'D' + 'C' * (cur // 100 - 5) + ns
        elif cur == 900:
            ns = 'CM' + ns
        elif cur < 4000:
            ns = 'M' * (cur // 1000) + ns

        return ns

    # 先从个位开始,表示每一位,再拼接起来
    ns, div = "", 10
    
    while True:
        # 这一位的数
        cur = num % div
        ns = handleNum(cur, ns)
        # 将这位去掉过后的整数
        num = num // div * div
        div *= 10

        # 最后一位,计算出来就结束了
        if num // div == 0:
            cur = num
            ns = handleNum(cur, ns)
            break

    return ns

解法二

参考力扣官方答案,采用贪心算法,贪心算法是一种在当前时间做出最佳可能决策的算法。这里既是取出最大可能的符合。
主要思想:为了表示一个给定的整数,我们寻找适合它的最大符号。我们减去它,然后寻找适合余数的最大符号,依此类推,直到余数为0。我们取出的每个符号都附加到输出的罗马数字字符串上。

def intToRoman2(num):

    digits = [(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")]
    # 记录罗马数字符号
    roman_digits = []
    # 从最大到最小循环遍历每个符号,检查当前符号的有多少个副本适合剩余的整数
    for value, symbol in digits:
        # 当数字取完了过后就结束遍历
        if num == 0: break
        # Return the tuple (x//y, x%y)
        count, num = divmod(num, value)
        # 将这位表示的罗马数字符号添加到数组里面
        roman_digits.append(symbol * count)
    # 返回拼接起来的字符串
    return "".join(roman_digits)
复杂度分析
  • 时间复杂度:O(1)。由于有一组有限的罗马数字,循环可以迭代多少次有一个硬上限。因此,我们说时间复杂度是常数的,即 O(1)。
  • 空间复杂度:O(1),使用的内存量不会随输入整数的大小而改变,因此是常数的。

解法三

参考力扣官方答案,将每一位都表示出来,然后再逐位进行计算和我的解法一有点相像。

主要思想:在代码中实现它最简单的方法是使用 4 个独立的数组;每个位置值对应一个数组。然后,在输入数字中提取每个位置的数字,在相关数组中查找它们的符号,并将它们全部附加在一起。

def intToRoman3(num):
    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"]
    return thousands[num // 1000] + hundreds[num % 1000 // 100] + tens[num % 100 // 10] + ones[num % 10]
复杂度分析
  • 时间复杂度:O(1)。无论输入的大小,都会执行相同数量的操作。因此,时间复杂度是常数的。
  • 空间复杂度:O(1),虽然我们使用数组,但不管输入的大小,它们都是相同的大小。因此,它们是常数级空间。

力扣官方答案

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容