一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
罗马数字包含以下七种字符:
I,V,X,L,C,D和M。给定一个罗马数字,将其转换成整数。
| 字符 | 数值 |
|---|---|
| I | 1 |
| V | 5 |
| X | 10 |
| L | 50 |
| C | 100 |
| D | 500 |
| M | 1000 |
此题要求将罗马数字转为整数,最笨的办法就是暴力枚举,举个例子:
LVIII
如何将LVIII换为数字呢?我们可以从开头进行遍历,当遍历到L时,计数器加50,遍历到V时,计数器加5,遍历到三个I时,计数器加3,最终结果为58。
若是出现特殊情况,如:
MCMXCIV
首先仍然从头开始遍历,当遍历到M时,计数器加1000,遍历到C时,计数器本应该加100,但是C后面是M,而CM表示的是900,所以我们应该将CM看作一个整体,包括后面的XC也是如此。 如何做到这一点呢?在遍历每一个符号时,我们只需观察下一个符号是否能够与其配对,比如遍历到C时,观察下一个符号M是否与其配对,发现CM配对到了,那么就直接将CM的值900加入,然后跳过M符号的遍历即可。
代码如下:
public static int romanToInt(String s) {
Map<String, Integer> map = new HashMap<>();
map.put("I", 1);
map.put("V", 5);
map.put("X", 10);
map.put("L", 50);
map.put("C", 100);
map.put("D", 500);
map.put("M", 1000);
map.put("IV", 4);
map.put("IX", 9);
map.put("XL", 40);
map.put("XC", 90);
map.put("CD", 400);
map.put("CM", 900);
int result = 0;
// 从头开始遍历
for (int i = 0; i < s.length(); ++i) {
// 寻找I的配对
if (s.charAt(i) == 'I') {
if ((i + 1) < s.length()) {
char c = s.charAt(i + 1);
switch (c) {
case 'V':
result += map.get("IV");
// 配对成功,让i++,跳过下一个符号
i++;
break;
case 'X':
result += map.get("IX");
i++;
break;
default:
result += map.get("I");
break;
}
}else {
// 若无配对,直接加I的值即可
result += map.get("I");
}
// 寻找X的配对
} else if (s.charAt(i) == 'X') {
if ((i + 1) < s.length()) {
char c = s.charAt(i + 1);
switch (c) {
case 'L':
result += map.get("XL");
i++;
break;
case 'C':
result += map.get("XC");
i++;
break;
default:
result += map.get("X");
break;
}
}else {
result += map.get("X");
}
// 寻找C的配对
} else if (s.charAt(i) == 'C') {
if ((i + 1) < s.length()) {
char c = s.charAt(i + 1);
switch (c) {
case 'D':
result += map.get("CD");
i++;
break;
case 'M':
result += map.get("CM");
i++;
break;
default:
result += map.get("C");
break;
}
}else {
result += map.get("C");
}
} else {
// 其它情况,直接加符号对应的数值即可
result += map.get(s.charAt(i) + "");
}
}
return result;
}
如上方法虽然能够解决问题,但是显得不够优雅,事实上,我们在对特殊情况的处理上花费了大量的时间,而特殊情况其实是有一定规律的,比如IV,它表示整数4,而I表示整数1,V表示整数5,既然如此,让V减I不就得到了IV的值吗? 再看IX,让X减I,即:10 - 1 = 9,其它情况也是如此。
那么根据这个规律,思路就变得简单了,对于LVIII,我们只需依次遍历,然后加入对应的整数值即可,而对于特殊情况MCMXCIV,我们首先要知道何时会出现特殊情况,不难发现,只有当右边的符号数值大于左边的时候才表示是特殊情况,根据此条件找到特殊情况,然后让大数值减去小数值即可得到特殊情况的值。
我们再来举个例子,MCM中,M表示1000,C表示100,所以其结果应该为1000 + (1000 - 100) = 1900,而事实上,我们根本不需要让M - C来单独得到CM的值,只需让M先减C再加M即可:1000 - 100 + 1000 = 1900,它们是等价的,所以代码得到了进一步的简化。
代码如下:
public static int romanToInt(String s) {
Map<Character, Integer> symbolValues = new HashMap<Character, Integer>() {{
put('I', 1);
put('V', 5);
put('X', 10);
put('L', 50);
put('C', 100);
put('D', 500);
put('M', 1000);
}};
int result = 0;
// 从头开始遍历
for (int i = 0; i < s.length(); ++i) {
// 得到当前符号表示的数值
int num = symbolValues.get(s.charAt(i));
// 防止下标越界
// 判断下一个符号表示的数值是否大于当前符号
if (i < s.length() - 1 && num < symbolValues.get(s.charAt(i + 1))) {
// 若大于,则此时为特殊情况
result -= num;
}else{
result += num;
}
}
return result;
}
此题得解。