Integer to Roman

Given an integer from 1 to 3999, convert it to a Roman numeral. The symbols are I = 1, V = 5, X = 10, L = 50, C = 100, D = 500 and M = 1000. A 4 or 9 in any place uses the subtractive forms IV, IX, XL, XC, CD and CM; I, X, C and M may repeat up to three times. This is LeetCode 12.

Example 1
Input: num = 3749
Output: "MMMDCCXLIX"
Explanation: 3000 = MMM, 700 = DCC, 40 = XL, 9 = IX.
Example 2
Input: num = 58
Output: "LVIII"
Explanation: 50 = L, 8 = VIII.
Example 3
Input: num = 1994
Output: "MCMXCIV"
Explanation: 1000 = M, 900 = CM, 90 = XC, 4 = IV.

How to Think About It

Step 1: Treat the subtractive forms as symbols too

The awkward cases, 4 and 9 in each place, become easy if CM, CD, XC, XL, IX and IV are simply extra symbols. That gives 13 symbols in all, from M = 1000 down to I = 1.

Step 2: Always take the largest symbol that fits

Go through the 13 symbols from largest to smallest. While the number is at least the symbol's value, write the symbol and subtract its value.

Step 3: Why greedy is right here

With the subtractive pairs in the table, every number has exactly one valid Roman form, and it is the one that uses the largest symbols first. For 1994, taking M leaves 994; the largest symbol that fits 994 is CM, not D, which gives the correct "MCM...".

Walkthrough

num = 1994.

RemainingLargest symbol that fitsWritten so far
1994M (1000)M
994CM (900)MCM
94XC (90)MCMXC
4IV (4)MCMXCIV

Java Solution

Two parallel arrays hold the 13 values and their symbols, largest first.

Java
public class IntegerToRoman {

    private static final int[] VALUES = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    private static final String[] SYMBOLS =
            {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

    public static String intToRoman(int num) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < VALUES.length; i++) {
            while (num >= VALUES[i]) {        // take the largest symbol that still fits
                sb.append(SYMBOLS[i]);
                num -= VALUES[i];
            }
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(intToRoman(3749));   // MMMDCCXLIX
        System.out.println(intToRoman(58));     // LVIII
        System.out.println(intToRoman(1994));   // MCMXCIV
    }
}

The original implementation, IntegerToRoman.java, is on GitHub.

Complexity

  • Time: O(1): the input is at most 3999, so the output has at most 15 symbols.
  • Space: O(1).

The reverse conversion is Roman to Integer.