数学LeeCode13. 罗马数字转整数

Posted chenry777

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数学LeeCode13. 罗马数字转整数相关的知识,希望对你有一定的参考价值。

罗马数字包含以下七种字符: IVXLCDM

字符          数值
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 的范围内。

在这里插入图片描述

题解一:遍历字符串,分别转换

常规思想遍历循环整个输入的字符串,对应每种情况的判断,最后输出结果

class Solution {
    // 题解一:遍历字符串,分别转换
    // 获取单个罗马字符的整数值
    public int getInt(char r) {
        int ans = 0;
        switch (r) {
            case 'I':
                ans = 1;
                break;
            case 'V':
                ans = 5;
                break;
            case 'X':
                ans = 10;
                break;
            case 'L':
                ans = 50;
                break;
            case 'C':
                ans = 100;
                break;
            case 'D':
                ans = 500;
                break;
            case 'M':
                ans = 1000;
                break;
            default:
        }
        return ans;
    }

    // 获取前后相连的特殊罗马字符对的整数值
    public int getInt(char r, char r_after) {
        int ans = 0;
        switch (r) {
            case 'I':
                ans = 1;
                break;
            case 'V':
                ans = 5;
                break;
            case 'X':
                ans = 10;
                break;
            case 'L':
                ans = 50;
                break;
            case 'C':
                ans = 100;
                break;
            case 'D':
                ans = 500;
                break;
            case 'M':
                ans = 1000;
                break;
            default:
        }
        if (r == 'I') {
            switch (r_after) {
                case 'V':
                    ans = 4;
                    break;
                case 'X':
                    ans = 9;
                    break;
                default:
            }
        }
        if (r == 'X') {
            switch (r_after) {
                case 'L':
                    ans = 40;
                    break;
                case 'C':
                    ans = 90;
                    break;
                default:
            }
        }
        if (r == 'C') {
            switch (r_after) {
                case 'D':
                    ans = 400;
                    break;
                case 'M':
                    ans = 900;
                    break;
                default:
            }
        }
        return ans;
    }

    // 判断2个输入字符是否构成特殊罗马字符
    public boolean isGetTwoInt(char r, char r_after) {
        if (r == 'I') {
            switch (r_after) {
                case 'V':
                case 'X':
                    return true;
                default:
            }
        }
        if (r == 'X') {
            switch (r_after) {
                case 'L':
                case 'C':
                    return true;
                default:
            }
        }
        if (r == 'C') {
            switch (r_after) {
                case 'D':
                case 'M':
                    return true;
                default:
            }
        }
        return false;
    }

    // 功能函数
    public int romanToInt(String s) {
        int ans = 0;
        // 循环length - 1次,例如s.length() = 20,遍历次数 = 19次,Max(i) = 18
        // , s.charAt(18)为倒数第二个数。
        // 该循环遍历了当s.length() >= 2时的所有情况
        for (int i = 0; i < s.length() - 1; i++) {
            ans += getInt(s.charAt(i), s.charAt(i + 1));
            //判断是否是两个字符的特殊情况
            if (isGetTwoInt(s.charAt(i), s.charAt(i + 1))) {
                i++;
            }
        }
        //将单字符情况和最后一个字符单独判断,如果放到上边的循环会越界
        // !s.length() >= 2 筛选单字符情况
        // s.length() - 2  ==>  倒数第二个字符
        // s.length() - 1  ==>  最后一个字符
        if (!(s.length() >= 2 && isGetTwoInt(s.charAt(s.length() - 2), s.charAt(s.length() - 1)))) {
            ans += getInt(s.charAt(s.length() - 1));
        }

        return ans;
    }
}

输入的字符串的length直接决定了循环的次数,所以时间复杂度是O(n),n为字符串长度

空间复杂度:O(1)。

题解二、提前去除特殊情况

class Solution {
    // 题解二、提前去除特殊情况
    public int romanToInt(String s) {
        int sum = 0;
        // 每次遇到特殊情况,就从sum中减去
        if (s.indexOf("IV") != -1) sum -= 2;
        if (s.indexOf("IX") != -1) sum -= 2;
        if (s.indexOf("XL") != -1) sum -= 20;
        if (s.indexOf("XC") != -1) sum -= 20;
        if (s.indexOf("CD") != -1) sum -= 200;
        if (s.indexOf("CM") != -1) sum -= 200;

        char c[] = s.toCharArray();
        // 遍历整个字符数组,
        for (int count = 0 ; count <= s.length() - 1; count++) {
            if (c[count] == 'M') sum += 1000;
            if (c[count] == 'D') sum += 500;
            if (c[count] == 'C') sum += 100;
            if (c[count] == 'L') sum += 50;
            if (c[count] == 'X') sum += 10;
            if (c[count] == 'V') sum += 5;
            if (c[count] == 'I') sum += 1;
        }
        return sum;
    }
}

时间复杂度还是O(n)

空间复杂度因为用到了一个char[],变成了O (n) ,也可以使用String.charAt()函数来代替String.toCharArray(),空间复杂度依然为O (1)

题解三、利用罗马数字的规则判定

利用到罗马数字的规则,一般情况是表示数字大的字母在前,数字小的字母在后,如果不是这样,就说明出现了特殊情况,此时应该做减法。

class Solution {
    // 题解三、利用罗马数字的规则判定
    private int getVal(char c) {
        switch (c) {
            case 'M':
                return 1000;
            case 'D':
                return 500;
            case 'C':
                return 100;
            case 'L':
                return 50;
            case 'X':
                return 10;
            case 'V':
                return 5;
            case 'I':
                return 1;
            default:
        }
        // 表示没有对应字符
        return 0;
    }

    public int romanToInt(String s) {
        int res = 0;
        // 空字符串判定
        if (s.length() == 0) {
            return res;
        }
        // 循环遍历:判断当前字符和next字符之间的大小关系
        for (int i = 0; i < s.length() - 1; i++) {
            int cur = getVal(s.charAt(i));
            int nex = getVal(s.charAt(i + 1));
            // 如果当前字符小于next字符,则res - 当前字符
            if (cur < nex) {
                res -= cur;
            } else {
                // 否则 + 当前字符
                res += cur;
            }
        }
        // 因为前面的循环只涉及到前后2个字符的比较,没有算上最后一个值
        // 最后一个值不存在next,所以肯定符合要求,直接加上
        return res + getVal(s.charAt(s.length() - 1));
    }
}

时间复杂度:O (1)
空间复杂度:O (1)

总结

这题的特殊情况相对较少,感觉用第二种方法来提前去除掉特殊值,会简单一点。

以上是关于数学LeeCode13. 罗马数字转整数的主要内容,如果未能解决你的问题,请参考以下文章

数学LeeCode7.整数反转

JavaScript笔试题(js高级代码片段)

leetcode-13.罗马数字转整数

数学LeeCode9.回文数

LeetCode 13. 罗马数字转整数

LeetCode 12 整数转罗马数字[硬编码 数学 找规律] HERODING的LeetCode之路