力扣经典算法篇-15-整数转罗马数字(自顶向下逐步分解)

发布于:2025-05-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

1、题干

七个不同的符号代表罗马数字,其值如下:

罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:
如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (V) 减 1 (I): IV ,9 是 10 (X) 减 1 (I):IX。仅使用以下减法形式:4 (IV),9 (IX),40 (XL),90 (XC),400 (CD) 和 900 (CM)。
只有 10 的次方(I, X, C, M)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V),50 (L) 或 500 (D)。如果需要将符号附加4次,请使用 减法形式。

给定一个整数,将其转换为罗马数字。
示例 1:
输入:num = 3749
输出: “MMMDCCXLIX”
解释:
3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M)
700 = DCC 由于 500 (D) + 100 © + 100 ©
40 = XL 由于 50 (L) 减 10 (X)
9 = IX 由于 10 (X) 减 1 (I)
注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位

示例 2:
输入:num = 58
输出:“LVIII”
解释:
50 = L
8 = VIII

示例 3:
输入:num = 1994
输出:“MCMXCIV”
解释:
1000 = M
900 = CM
90 = XC
4 = IV

提示:
1 <= num <= 3999

2、解题

方法一(自顶向下,逐步分解)

上一题我们解答了罗马数字转数字,本题是相反的过程。可以根据数据的特征从上到下逐步分解处理。
转化规则结合题目可知,可以通过全局Map的形式存储起来。从最高位千位,在到百位,十位和个位逐步转化处理。
转化过程中,需要注意4和9的特殊转换,如果大于5还要注意添加1次5的转化,剩下的就逐个按照最小累加。

代码示例:

static Map<Integer, String> int2CharMap = new HashMap<Integer, String>() {{   // 映射存储
        put(1, "I");
        put(5, "V");
        put(10, "X");
        put(50, "L");
        put(100, "C");
        put(500, "D");
        put(1000, "M");
        put(4, "IV");
        put(9, "IX");
        put(40, "XL");
        put(90, "XC");
        put(400, "CD");
        put(900, "CM");
    }};

    static List<Integer> numList = new ArrayList<Integer>() {{   // 处理顺序
        add(1000);
        add(100);
        add(10);
        add(1);
    }};

    public static String intToRoman(int num) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < numList.size(); i++) {
            Integer tempNum = numList.get(i);   // 待转化部分的权值
            int front = num / tempNum;   // 整数部分为待转化的部分
            if (front > 0) {
                if (front == 4 || front == 9) {
                    builder.append(int2CharMap.get(front * tempNum));      // 4和9的转换
                } else {
                    if (front > 4) {
                        builder.append(int2CharMap.get(tempNum * 5));   // 大于4,非9需要处理依次-5的信息
                        front = front - 5;
                    }
                    for (int j = 0; j < front; j++) {    // 之后的剩余按照最小补充
                        builder.append(int2CharMap.get(tempNum));
                    }
                }
            }
            num = num % tempNum;  // 余数即剩下未处理的部分
        }
        return builder.toString();
    }

方法二(自顶向下,逐步分解)

除了按照千,百,十,个的逐步分解外,还可以按照递减的思路。从高到低向下处理,能处理多高就处理多高,处理不了在向下分析。思路和方法一类似,只不过向下的方式不一样。
如:2816,题干最大1000,就先处理1000的,处理1次,得到1个M,一共处理2次;在处理900,不足则跳过;在处理500,可以处理1次得到1个D…

代码示例:

static int[] values = new int[]{1000,900,500,400,100,90,50,40,10,9,5,4,1};
    static String[] strs = new String[]{"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
    public static String intToRoman(int num) {
        StringBuilder builder =  new StringBuilder();
        for (int i = 0; i < values.length; i++) {
            int value = values[i];
            String str = strs[i];
            while (num>=value){
                builder.append(str);
                num = num-value;
            }
            if(num==0){
                break;
            }
        }
        return builder.toString();
    }

向阳出发,Dare To Be!!!