Browse curriculum

Integer to Roman

Convert an integer to a Roman numeral greedily: walk a 13-entry value table, subtraction pairs included, and write each symbol while it fits.

Problem Understanding

Integer to Roman: given an integer from 1 to 3999, return its Roman numeral. Symbols are I 1, V 5, X 10, L 50, C 100, D 500, M 1000, written largest first, with six subtraction pairs: IV 4, IX 9, XL 40, XC 90, CD 400, CM 900.

Example: 1994 returns "MCMXCIV".

The Intuition: Largest Value That Fits

This is a textbook greedy algorithm: at every step take the largest value that still fits, like paying with the largest coin first.

The only trick is the table. Put the subtraction pairs in as values of their own, so it has 13 entries:

1000 M, 900 CM, 500 D, 400 CD, 100 C, 90 XC, 50 L, 40 XL, 10 X, 9 IX, 5 V, 4 IV, 1 I

Then walk it once: while num ≥ value, write the symbol and subtract. For 4 the greedy choice is IV, never IIII. The reverse direction is Roman to Integer.

Interactive Walkthrough

The 13-entry table is on top, with the value being tried filled; the numeral grows below it. Try Longest to watch the same value taken several times in a row.

Dry Run Table

Input: num = 1994

Value tried Fits? Written num after
1000 M yes M 994
900 CM yes CM 94
500 … 100 no — 94
90 XC yes XC 4
50 … 5 no — 4
4 IV yes IV 0

Answer: "MCMXCIV".

The Solution Template

Two parallel arrays for the table and a greedy loop.

Integer to Roman Code
1var intToRoman = function(num) {
2 const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
3 const symbols = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
4 let out = '';
5 for (let i = 0; i < values.length; i++) {
6 while (num >= values[i]) {
7 out += symbols[i];
8 num -= values[i];
9 }
10 }
11 return out;
12};

Edge Cases & Common Mistakes

  • Leaving out the subtraction pairs: produces IIII and DCCCC, which are not valid.
  • Using if instead of while: values like M or I can repeat up to three times.
  • Wrong table order: the table must be sorted from largest to smallest, with CM between M and D.
  • Buffer size in C: the longest numeral, 3888, has 15 characters; allocate at least 16 bytes.
  • Digit-by-digit lookup: an alternative is four lookup tables (thousands, hundreds, tens, ones) indexed by each digit; same O(1).

Summary

List the 13 Roman values, subtraction pairs included, from largest to smallest. Walk the list and, while the number is at least the current value, append its symbol and subtract. O(1) time and space for inputs up to 3999.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Build the value table

    List 1000 M, 900 CM, 500 D, 400 CD, 100 C, 90 XC, 50 L, 40 XL, 10 X, 9 IX, 5 V, 4 IV, 1 I, largest first.

  2. Take values greedily

    For each value, while num is at least the value, append its symbol and subtract the value.

  3. Return the symbols

    Once num reaches 0, the appended symbols are the Roman numeral.

Frequently asked questions

Why does a greedy approach work for Integer to Roman?

Roman numerals are written largest value first, and each place (thousands, hundreds, tens, ones) is independent. Taking the largest value that still fits at every step produces exactly the canonical numeral, as long as the six subtraction pairs are in the table as values of their own.


Why include 900, 400, 90, 40, 9 and 4 in the table?

Without them, greed would write 4 as IIII and 900 as DCCCC. Listing CM, CD, XC, XL, IX and IV as values makes the greedy choice pick the subtraction form, so no special cases are needed.


What is the time complexity of Integer to Roman?

O(1). The input is at most 3999, the table has 13 entries, and the longest numeral (3888, MMMDCCCLXXXVIII) has 15 symbols, so the work is bounded by a constant.

Applying for software engineering roles? Free, no sign-up:

AI resume checker — match your resume to the job description