罗马数字包含以下七种字符: 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:
输入: "III"
输出: 3
示例 2:输入: "IV"
输出: 4
示例 3:输入: "IX"
输出: 9
示例 4:输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IC 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
审题:
1、只有6种情况是相减,其他相加
2、不管6种特殊情况是否出现,罗马数字都是叠加的
我的答案:
class Solution:
def romanToInt(self, s: str) -> int:
dict = {}
dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
list = ['IV', 'IX', 'XL', 'XC', 'CD', 'CM']
part = 0
s_split = []
for l in range(6):
if s.find(list[l]) != -1:
de_index = s.find(list[l])
part += dict[list[l][1]] - dict[list[l][0]]
s = s.replace(s[de_index]+s[de_index+1],'')
else:
pass
for i in range(len(s)):
s_split.append(s[i])
num = part
for j in s_split:
num += dict[j]
return num
基本思路:先筛选出特殊情况,算出减法部分的数字,同时在原字符串中去掉减法部分,只留加法部分。之后处理加法部分,即分割字符串,对应到字典再相加即可。
注意:新建空列表、设置初始值一定要放在循环之外!!
其他解法:
class Solution:
def romanToInt(self, s: str) -> int:
dict = {}
dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
s_split = []
num = 0
for i in range(len(s)):
s_split.append(s[i])
for j in range(len(s)):
try:
value_j = dict[s_split[j]]
except Exception:
value_j = 0
try:
value_J = dict[s_split[j + 1]]
except Exception:
value_J = 0
if value_j <value_J:
num += (value_J - value_j)
s_split.pop(j + 1)
else:
num += value_j
return num
在官网看大神解答是用java写的,自己按思路重写了一遍,运行成功,但发现几处问题。
基本思路:遍历然后比较每个数和后一个数的大小,如果大数在前则做加法,如果大数在后则做减法。
问题:
1、大数在后,处理的单元为2,如何在循环中跳过下一次循环?这里用了pop加异常处理的方式
2、另一处异常处理来自j+1在最后一位会超出循环,同样叠加了异常处理
最大的问题:结果性能不升反降……
重新调试:
发现前<后时,不需要用后-前这个死板的方法,对公式做个小小的变形(我咋就没想到呢),前面一个数字减,后面一个数字加,就不会出现后一位重复加的问题了。
改过一轮的代码如下,性能有略微提升:
class Solution:
def romanToInt(self, s: str) -> int:
dict = {}
dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
s_split = []
num = 0
for i in range(len(s)):
s_split.append(s[i])
for j in range(len(s)-1):
if dict[s_split[j]] >= dict[s_split[j+1]]:
num += dict[s_split[j]]
else:
num -= dict[s_split[j]]
num += dict[s_split[len(s)-1]]
return num
其他解法:使用get函数和enumerate遍历(优势在于写法简练)
1、除了第0位以外,每次取本位和前一位双元素。如果按原值取和会造成双元素前位取两遍,所以双元素在字典中的值要多减一个前位。(同样是数学变形的思路)
2、get函数的优势在于可以二选一,也就是如果没有找到该键值,则用默认值替代,这里就是如果字典里没有找到对应双元素,就取本位的值用于相加。
class Solution:
def romanToInt(self, s: str) -> int:
dict = {}
dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
dict['IV'],dict['IX'],dict['XL'],dict['XC'],dict['CD'],dict['CM'] = 3,8,30,80,300,800
for i, n in enumerate(s):
print(s[max(i-1,0):i+1])
print(dict.get(s[max(i-1,0):i+1],dict[n]))
return sum(dict.get(s[max(i-1,0):i+1],dict[n]) for i,n in enumerate(s))