罗马数字包含以下七种字符: 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:

输入: s = "III"
输出: 3
示例 2:

输入: s = "IV"
输出: 4
示例 3:

输入: s = "IX"
输出: 9
示例 4:

输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:

输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.


提示:

1 <= s.length <= 15
s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。

解题流程

这条题目是上一条题目的反转,上一条由数字转罗马数字,这一条由罗马数字转数字,那么其实按照之前的逻辑一样可以使用,就是将所有的情况都罗列出来,当字符串一个个比对,如果符合就往结果中
加这个字符对应的数字,同时将字符串中的字符删掉。如果没有匹配到的话就去匹配后面的字符,直至s字符串被匹配完。

代码:

var romanToInt = function(s) {
//列出所有的字符情况
const charArr = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']
//字符对应的数字
const numberArr = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
let result = 0

while (s.length > 0) {
//循环匹配
for (let i = 0; i < charArr.length; i++) {
//匹配上了
if (s.startsWith(charArr[i])) {
//把对应数字加到结果中去
result += numberArr[i]
//从s字符串中删除对应匹配上的子串
if (charArr[i].length === 1) {
s = s.substring(1)
}else {
s = s.substring(2)
}
}
}
}
return result
};

写完提交,超越95%的人,但是上面的解法并不完美,因为在这个过程有重复循环,比如”MMM”就需要将整个charArr循环三次才能匹配到,那么如何才能只循环一遍呢?
我们最希望的就是去循环s字符串,但是难点在于4,9开头的那些数字,因为这些数字都需要用两个字符表示。如何才能用一个字符就表示4,9呢?观察它们之间的关系,
我们发现,如果后面的字符比前面的字符代表的数字大,那么就说明这两个字符应该合起来表示为一个数,即1,5表示4,那就可以表示为(-1 + 5)。那代码逻辑就
很简单了,每次遍历到一个字符,就看这个字符的后一个字符是否代表的数字比当前字符大,如果大,那么需要减去当前字符代表的值,如果小,那么直接加上代表的值。

代码

var romanToInt = function(s) {
//创建对应字符与值的map
const romanMap = {
'I': 1, 'V': 5, 'X': 10,
'L': 50, 'C': 100,
'D': 500, 'M': 1000
};

let total = 0;

//遍历s字符串
for (let i = 0; i < s.length; i++) {
//获取当前字符代表的值
const current = romanMap[s[i]];
//获取后一个字符代表的值
const next = romanMap[s[i + 1]];

//如果下一个字符比当前字符代表的值大,说明两个字符组成一个数字,需先减去当前值
if (next > current) {
total -= current;
} else {
//如果不是的话,就先加上当前值
total += current;
}
}

return total;
};