題目描述
羅馬數字包含以下七種字元: 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解題得的原始碼,解題思路在程式碼的註釋裡。
- 我的leetcode主頁https://leetcode-cn.com/u/boywithacoin_cn/
- 我的個人部落格https://boywithacoin.cn/
- github_所有原始碼網址https://github.com/Freen247/leetcode
本作品採用《CC 協議》,轉載必須註明作者和本文連結