leetcode 題解 12python3@ 透過使用羅馬數字的最單元位來構造陣列 + 構造數字演算法

娃哈哈店長發表於2020-02-19

題目描述

羅馬數字包含以下七種字元: 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 的範圍內。

解題思路

由題意可知,從整數轉化為羅馬數字的時候,每個位上的數字(0~9)都是由四種型別的羅馬數字組成的
比如個位數:

  • '(null)'
  • I
  • V
  • X

透過此思路,我們可以構造一個二維list。

self.Glossary = [
            ['I','V','X'],
            ['X','L','C'],
            ['C','D','M'],
            ['M'],
        ]

傲嬌臉(感覺這種思路更深一點)

相比其他巨佬(我是菜雞)的題解中構造的陣列,比如@liweiwei1419題解

nums = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
romans = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]

這種思路相當於是將每個位數(比如個位數)的1-4-5-9給單獨列出來了。其中1 5 10 是構造數字的單元(最底層)的單元字元,而因為4和9的構造規則和123 678的規則不一樣,所以題主將1-4-5-9單獨列出來再進行演算法匹配,就更順暢。這位巨佬的每個位數需要構造4個字元,但是上述的構造方法只需要定義三個單元字元。

在評論區也看到以為巨佬用的這種構造方法

m = [
            ['', 'M', 'MM', 'MMM'],
            ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'],
            ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
            ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
        ]

可能大家看到這種的時候會想,這麼直接的嗎,把每個位數的1-9全部列出來了,但是我感覺這種方法相比上面第一個巨佬的方法更好一些,因為這種方法的思路比較清晰:我分位數來構造陣列,將每個位的所有出現的情況全部列出來,這種方式就相當於我們先花最大的空間來構造我們需要的(類似雜湊表吧),然後再用複雜度比較低的演算法來進行簡單的匹配。

class Solution:
    def intToRoman(self, num):
        m = [
            ['', 'M', 'MM', 'MMM'],
            ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'],
            ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
            ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
        ]
        d = [1000, 100, 10, 1]
        r = ''
        for k, v in enumerate(d):
            r += m[k][int(num/v)]
            num = num % v
        return r

妙啊,點個贊。(這種思路將演算法最簡單化,但是空間的使用也是最大的)

好了,上述引用的兩個大佬,我真的覺得很牛逼,將空間略微複雜化瞭然後使用最巧妙地演算法。

那使用這種最底層地單元化地構造方法:

self.Glossary = [
            ['I','V','X'],
            ['X','L','C'],
            ['C','D','M'],
            ['M'],
        ]

如何來寫演算法呢

羅馬數字地構造思路:
在每一個(十分)位上

  • 用1單元位來疊加構造1-3
    • 比如I,II,III 或者X,XX,XXX
  • 用1和5單元位來構造4
    • 比如IV 或者XL
  • 用5來直接使用
    • 比如V 或者L
  • 用1和5來構造6-8
    • 比如VI,VII,VIII或者LX,LXX,LXXX
  • 用1和10來構造9
    • 比如IX 或者XC

說到這裡其實,羅馬數字是一種5進位制地數,(個人認為哈)。

但是由於我們地整數時10進位制地,所以4和9地構造相當於就是兩種不同地構造方法(用此十分位的1和5來構造、用此此十分位的1和下個十分位的10來構造)

那我們需要將這幾個特例拿出來:

while _<x:
    res+=v[0]
    _+=1
    if _==4:
        res=v[0]+v[1]
    elif _==5:
        res=v[1]
    elif _==9:
        res=v[0]+v[2]

具體程式碼如下:

    def viaDict(self, num:int) -> str:
        resl,nlist=[],list(str(num))
        for k,v in enumerate(self.Glossary):
            try:
                _,res,x=0,'',int(nlist.pop())
                while _<x:
                    res+=v[0]
                    _+=1
                    if _==4:
                        res=v[0]+v[1]
                    elif _==5:
                        res=v[1]
                    elif _==9:
                        res=v[0]+v[2]
                resl.insert(0,res)
            except (StopIteration,IndexError):
                # 遇到StopIteration就退出迴圈
                break
        return "".join(resl)

程式碼

#
# @lc app=leetcode.cn id=12 lang=python3
#
# [12] 整數轉羅馬數字
#

# @lc code=start
class Solution:
    def __init__(self):
        self.Glossary = [
            ['I','V','X'],
            ['X','L','C'],
            ['C','D','M'],
            ['M'],
        ]
    def viaDict(self, num:int) -> str:
        resl=[]
        # get the Iterator Object first:
        nlist = list(str(num))
        for k,v in enumerate(self.Glossary):
            try:
                _=0
                res=''
                x=int(nlist.pop())
                while _<x:
                    res+=v[0]
                    _+=1
                    if _==4:
                        res=v[0]+v[1]
                    elif _==5:
                        res=v[1]
                    elif _==9:
                        res=v[0]+v[2]
                resl.insert(0,res)
            except (StopIteration,IndexError):
                # 遇到StopIteration就退出迴圈
                break
        return "".join(resl)
    def intToRoman(self, num: int) -> str:
        return {
            1:lambda num:self.viaDict(num),
        }[1](num)
# @lc code=end

if __name__ == "__main__":
    solve = Solution()
    # print(solve.Glossary.items())
    print(solve.intToRoman(444))

一百個人讀哈姆雷特就有一百個哈姆雷特。

leetcode

leetcode解題得的原始碼,解題思路在程式碼的註釋裡。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
文章!!首發於我的部落格Stray_Camel(^U^)ノ~YO

相關文章