LeetCode 13. 罗马数字转整数

140 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情

罗马数字包含以下七种字符: I, V, X, LCD 和 M

给定一个罗马数字,将其转换成整数。

字符数值
I1
V5
X10
L50
C100
D500
M1000

此题要求将罗马数字转为整数,最笨的办法就是暴力枚举,举个例子:

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;
}

此题得解。