在某些情况下 Roman 到 Int 的转换不正确
Roman to Int converts incorrectly in some cases
我的函数如下所示:
let romanToInt = romanNumber => {
if(typeof romanNumber !== 'string') throw new TypeError('Argument must be of type String');
const values = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000 };
let sum = 0;
romanNumber.split('').map(n => n.toUpperCase()).forEach(n => sum = (sum >= values[n]) ? sum + values[n] : values[n] - sum);
return sum;
}
console.log(romanToInt("MCMXCVI"));
我测试的大多数输入都是正确的,但是罗马数字 MCMXCVI
例如应该给我 1996
,而不是 2216
,这就是我的意思得到。
我找到了这个,但我不确定如何实现它:
You must separate ones, tens, hundreds, and thousands as separate
items. That means that 99 is XCIX, 90 + 9, but never should be written
as IC. Similarly, 999 cannot be IM and 1999 cannot be MIM.
您不应该希望通过简单的映射来解决问题,因为在罗马数字中,数字的含义取决于上下文。我认为,很多情况都可以用类似的方式处理(而不是你的 split 和 map 行):
var digits = romanNumber.split('');
var i = 0;
while (i < digits.length) {
if (i == digits.length - 1 || values[digits[i]] >= values[digits[i+1]]) {
sum += values[digits[i]];
i++;
}
else {
sum += values[digits[i+1]] - values[digits[i]];
i += 2;
}
}
但我不确定它是否适用于所有情况。最好看看它是如何在现成的库中实现的,也许对于其他语言。例如,这里是 Perl 的实现:http://search.cpan.org/~chorny/Roman-1.24/lib/Roman.pm
我认为您不能使用单行映射来实现,因为该操作取决于相邻字符的比较。然而,用一个简单的for
循环:
var chars = romanNumber.split('');
var sum = values[chars[chars.length - 1]];
for (var i = chars.length - 2; i >= 0; i--) {
if (values[chars[i + 1]] <= values[chars[i]]) {
sum += values[chars[i]];
} else {
sum -= values[chars[i]];
}
}
问题是当一个罗马数字的值小于它右边的数字时,你想从总和中减去它而不是加。
根据你的问题,你需要满足不同的项目。一种简单的方法是简单地使您的值集更大,并寻找多字符匹配项。这是可能的,因为罗马数字只允许几种组合。我把一个fiddle放在一起here
const values = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
'CM': 900,
'CD': 400,
'XC': 90,
'XL': 40,
'IX': 9,
'IV': 4
};
let sum = 0;
while(romanNumber.length > 0){
let piece = romanNumber.substring(0,2);
if(values[piece]){
sum += values[piece];
romanNumber = romanNumber.substring(2);
}else if(values[piece[0]]){
sum += values[piece[0]];
romanNumber = romanNumber.substring(1);
}
}
return sum;
我的函数如下所示:
let romanToInt = romanNumber => {
if(typeof romanNumber !== 'string') throw new TypeError('Argument must be of type String');
const values = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000 };
let sum = 0;
romanNumber.split('').map(n => n.toUpperCase()).forEach(n => sum = (sum >= values[n]) ? sum + values[n] : values[n] - sum);
return sum;
}
console.log(romanToInt("MCMXCVI"));
我测试的大多数输入都是正确的,但是罗马数字 MCMXCVI
例如应该给我 1996
,而不是 2216
,这就是我的意思得到。
我找到了这个,但我不确定如何实现它:
You must separate ones, tens, hundreds, and thousands as separate items. That means that 99 is XCIX, 90 + 9, but never should be written as IC. Similarly, 999 cannot be IM and 1999 cannot be MIM.
您不应该希望通过简单的映射来解决问题,因为在罗马数字中,数字的含义取决于上下文。我认为,很多情况都可以用类似的方式处理(而不是你的 split 和 map 行):
var digits = romanNumber.split('');
var i = 0;
while (i < digits.length) {
if (i == digits.length - 1 || values[digits[i]] >= values[digits[i+1]]) {
sum += values[digits[i]];
i++;
}
else {
sum += values[digits[i+1]] - values[digits[i]];
i += 2;
}
}
但我不确定它是否适用于所有情况。最好看看它是如何在现成的库中实现的,也许对于其他语言。例如,这里是 Perl 的实现:http://search.cpan.org/~chorny/Roman-1.24/lib/Roman.pm
我认为您不能使用单行映射来实现,因为该操作取决于相邻字符的比较。然而,用一个简单的for
循环:
var chars = romanNumber.split('');
var sum = values[chars[chars.length - 1]];
for (var i = chars.length - 2; i >= 0; i--) {
if (values[chars[i + 1]] <= values[chars[i]]) {
sum += values[chars[i]];
} else {
sum -= values[chars[i]];
}
}
问题是当一个罗马数字的值小于它右边的数字时,你想从总和中减去它而不是加。
根据你的问题,你需要满足不同的项目。一种简单的方法是简单地使您的值集更大,并寻找多字符匹配项。这是可能的,因为罗马数字只允许几种组合。我把一个fiddle放在一起here
const values = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
'CM': 900,
'CD': 400,
'XC': 90,
'XL': 40,
'IX': 9,
'IV': 4
};
let sum = 0;
while(romanNumber.length > 0){
let piece = romanNumber.substring(0,2);
if(values[piece]){
sum += values[piece];
romanNumber = romanNumber.substring(2);
}else if(values[piece[0]]){
sum += values[piece[0]];
romanNumber = romanNumber.substring(1);
}
}
return sum;