LeetCode 整數轉羅馬數字
題目:羅馬數字包含以下七種字元: 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:
輸入: 58 輸出: "LVIII" 解釋: C = 100, L = 50, XXX = 30, III = 3.
示例 3:
輸入: 1994 輸出: "MCMXCIV" 解釋: M = 1000, CM = 900, XC = 90, IV = 4.
思路:定義兩個函式:函式一、將輸入的數字按每一位分解,如1994分解得到1000,900,90,4。函式二、先找到能表示輸入數字的最大基本符號,再用該數值對基本符號求餘數,得到的新值回代到函式中再次計算(如,8的最大基本符號為V,8對5求宇得到3,3回代到函式中得到III,拼接後就能得到VIII)。
函式一:將輸入的數字num轉為string形式,呼叫enumerate函式訪問其數值部分的每一位(如輸入94,就迴圈兩次,tm_num在兩次迴圈中的值分別為90,4)。對於每一次得到的臨時值tm_num,傳入cal函式中,計算得到對應的字串(如90,將返回''XC'')。最後將每次迴圈得到的字串相加,就能得到我們想要的字串了。
函式二:對於一個輸入,我們需要先找到該數值的最大基本符號,因為事先不知道它是多少位的,所以輸入值依次跟1000,500,100....1比較找到其最大基本符號。這一部的實現方法有兩種,迴圈或者遞迴,我是用遞迴實現的,通過控制變數i,j來表示1000,500..1,其中的規律為
比較值的分解 | i | j |
1000=2^3+5^3 | 3 | 3 |
500 =2^2+5^3 | 2 | 3 |
100 =2^2+5^2 | 2 | 2 |
50 =2^1+5^2 | 1 | 2 |
10 =2^1+5^1 | 1 | 1 |
5 =2^0+5^1 | 0 | 1 |
1 =2^0+5^0 | 0 | 0 |
先將係數i,j初始值設為0,0,等於3,3時結束,判斷兩個值是否相等,相等則j自增1,j>i則i自增1,該規律可以很好地對應上表。以輸入值8為例,在我們的程式碼裡面比較值記為temp,通過與temp的比較,可以確定找到8的最大基本符號值為5,通過字典(結構如下)可以找到最大基本符號。接下來再將8的求餘結果3回代到函式中,求出其餘數部分對應的字串。
{'1':'I','5':'V','10':'X','50':'L','100':'C','500':'D','1000':'M'}
因為題目有要求要對4,9這樣的數字進行轉換,按照上面的部分,對4的輸出結果為:IIII。因此需要一些額外的判斷和處理,觀察發現這些數都有一個特徵:加上一個值,就等於基本符號了。但是4,9需要加一個1,40、90需要加一個10。實際上我們可以使用上表定義的變數i,計算得到的10^i這就是我們需要增加的那個量。只要對一個輸入加一個判斷,當加上這樣一個量時是否為一個基本符號,就能找出所有的特殊情況。對於4,或者9這樣的數,因為其對應的字串為IV、IX。以IV為例,可以將字串分為兩部分I和V,找到它的基本單位值1(就是上面的10^i),列印這個值對應的羅馬數字I,然後將4+1傳入遞迴函式中,得到字串5。相應的,40就是先找到字元X,再將50傳回遞迴函式,得到L。
以上,我們就對一般情況和特殊情況都做了處理。下面為具體的程式碼實現,有詳細的註釋:
class Solution(object):
def intToRoman(self, num, i=0, j=0):
"""
:type num: int
:rtype: str
"""
strx=''
str_num=str(num) #將數字轉為字串
for indx,value in enumerate(str_num): #遍歷str_num
tm_num=int(str_num[indx])*10**(len(str_num)-(indx+1)) #tm_num表示num分解後的的每一位
strx+=self.cal(tm_num) #將每一位對應值代入cal函式中,計算得到對應的羅馬數字字串
return strx #返回結果
def cal(self,num,i=0,j=0):
str1=''
str2=''
val={'1':'I','5':'V','10':'X','50':'L','100':'C','500':'D','1000':'M'} #羅馬字元與阿拉伯數值對應轉換字典
temp=1000/(2**i*5**j) #temp為羅馬數字基本符號值,由i,j決定,從1000-500..5-1
base_value=10**(3-i) #基本值,當前數值對應的基本量,如4對應的1,40對應10
if(i==j): #這裡的if和elif是對特殊情況的處理,因為i,j是從0,0到3,3.
tm_i=i #對於特殊情況的輸入,需要還原出其的上一級對應的i,j
tm_j=j-1 #以4為例,滿足4>1時,i和j的值為3,3.因為要輸出IV.I可以由base_value確定
i+=1 #V可以將4+1回代cal函式得到5對應的V,因為此時i,j值對應的最大基本符號為I
elif(i>j): #所以需要對i,j處理一下,對應最大基本符號V,並與4+1一起回代到cal中
tm_i=i-1
tm_j=j
j+=1
if(num>=temp): #找出num的最大基本符號
if((base_value+num)==(1000/(2**tm_i*5**tm_j))): #判斷一個值是否為特殊情況,即,num+base_value是否等於基本符號
str1=val['%d'%base_value] #將基本符號對應的字元放入str1中,如IX,將I放入str1中
num+=base_value #將num更新為num+base_value,如,num=40 -> num=50
i=tm_i #更新i,j的值
j=tm_j
else: #一般情況下
str1=val['%d'%temp]*(num/temp) #str1為num的基本字元*次數,如3,則str1='III'
num=num%temp #num更新為num的餘數
if((i<4)&(j<4)): #i,j的變化範圍
str2=self.cal(num,i,j) #新的num,i,j回代到cal中,計算得到str2
return str1+str2 #返回結果