罗马数字转整数功能的实现
题目
# 罗马数字包含以下七种字符: 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。 IV=4 IX=9
# X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 XL=40 XC=90
# C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。CD=400 CM=900
# 给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
#
# 来源:力扣(LeetCode)
# 链接:https://leetcode-cn.com/problems/roman-to-integer
# 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
拿到这道题时,我首先想的是建立一个roman数列,里面从小到大保存着所有的罗马字符,同时建立一个字典从小到大保存着罗马字符以及其对应的数量大小。如下所示:
roman = ['I', 'V', 'X', 'L', 'C', 'D', 'M']
roman_value = {'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000
}
解题思路则是,将拿到的罗马数字用for循环取值,一次取两个。若是取到的第一个字符比第二个字符小,则加上第二个字符代表的值减去第一个字符代表的值(如IX=10-1=9),否则加上第二个数字的值。然后再判断第二个字符和第三个字符......
实现算法
出错算法
首次解题时,我写出了如下算法:
def romanToInt(s):
res = 0
for i in s: # i:第一个数
if s.index(i) != len(s) + 1:
j = s[s.index(i) + 1] #出错 由于X出现了两次,当取i='X'时,index(i)会默认取第一个'X'的索引
if roman_value[i] < roman_value[j]:
res += roman_value[j] - roman_value[i]
res -= roman_value[j]
# 由于第二次循环还是会循环到j,相当于再加了一次j,所以直接先减去以抵消
else:
res += roman_value[i]
else:
res += roman_value[i]
return res
在转换‘MXCIX’,也就是罗马字符的1099时,输出的答案为1079。然后我debug出了一个我无法解决bug。
我使用了.index的方法,也就是根据每次循环取到的具体字符 i 来反推这个字符在原字符串 s 中的索引,并且根据这个索引+1的值来取到下一个值.但是我在使用这种方法的时候没有考虑到字符可能重复出现的情况.就比如在转换‘MXCIX’时,当 i 取到第二个X(高亮X)时, s.index(i)任然是第一个X的索引,导致程序出错.
这个bug以我现在的知识无法解决,于是我在查阅一些资料后写出了改进算法:
改进算法
def romanToInt(s):
res = 0
for i,v in enumerate(s): # i:索引 v:值
if i != len(s)-1:
j = s[i + 1] #j: s中以i+1为索引的值
if roman_value[v] < roman_value[j]:
res += roman_value[j] - roman_value[v]
res -= roman_value[j]
else:
res += roman_value[v]
else:
res += roman_value[v]
return res
主要改进是使用了enumerate(枚举)这个方法,它的作用是分别返回索引以及值.这样就在循环的一开始确定了索引和值,而不是通过值反推索引,这样会因为重复出现的值出bug.
反思:enumerate真好真好用啊