Integer to Roman
MedPrompt
Seven different symbols represent Roman numerals with the following values:
| Symbol | Value |
|---|---|
| I | 1 |
| V | 5 |
| X | 10 |
| L | 50 |
| C | 100 |
| D | 500 |
| M | 1000 |
Roman numerals are formed by appending the conversions of decimal place values from highest to lowest. Converting a decimal place value into a Roman numeral has the following rules:
- If the value does not start with 4 or 9, select the symbol of the maximal value that can be subtracted from the input, append that symbol to the result, subtract its value, and convert the remainder to a Roman numeral.
- If the value starts with 4 or 9 use the subtractive form representing one symbol subtracted from the following symbol, for example, 4 is 1 (
I) less than 5 (V):IVand 9 is 1 (I) less than 10 (X):IX. Only the following subtractive forms are used: 4 (IV), 9 (IX), 40 (XL), 90 (XC), 400 (CD) and 900 (CM). - Only powers of 10 (
I,X,C,M) can be appended consecutively at most 3 times to represent multiples of 10. You cannot append 5 (V), 50 (L), or 500 (D) multiple times. If you need to append a symbol 4 times use the subtractive form.
Given an integer, convert it to a Roman numeral.
Example 1:
Input: num = 3749
Output: "MMMDCCXLIX"
Explanation:
3000 = MMM as 1000 (M) + 1000 (M) + 1000 (M)
700 = DCC as 500 (D) + 100 (C) + 100 (C)
40 = XL as 10 (X) less of 50 (L)
9 = IX as 1 (I) less of 10 (X)
Note: 49 is not 1 (I) less of 50 (L) because the conversion is based on decimal placesExample 2:
Input: num = 58
Output: "LVIII"
Explanation:
50 = L
8 = VIIIExample 3:
Input: num = 1994
Output: "MCMXCIV"
Explanation:
1000 = M
900 = CM
90 = XC
4 = IV
Constraints:
1 <= num <= 3999
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves iterating through a pre-defined list of Roman symbols and their corresponding integer values, from largest to smallest. For each symbol, we greedily subtract its value from the input number as many times as possible, appending the symbol to our result each time. To handle subtractive cases like 4 (IV) and 9 (IX), we must include these combinations (e.g., 900 for CM, 400 for CD) in our list of symbols.
Algorithm
-
- Define two arrays,
valuesandsymbols, which map integer values to their Roman numeral representations. Include subtractive forms (e.g., 900 -> "CM"). Sort them in descending order of value.
- Define two arrays,
-
- Initialize an empty
StringBuilderto build the result.
- Initialize an empty
-
- Iterate through the
valuesarray from the beginning (largest value).
- Iterate through the
-
- For each value
values[i], use awhileloop to check if the input numbernumis greater than or equal to it.
- For each value
-
- Inside the
whileloop, append the correspondingsymbols[i]to theStringBuilderand subtractvalues[i]fromnum.
- Inside the
-
- Continue this process until
numbecomes 0.
- Continue this process until
-
- Convert the
StringBuilderto a string and return it.
- Convert the
Walkthrough
We create two parallel arrays: one for the integer values and one for the corresponding Roman symbols. These arrays must be sorted in descending order of value. It's crucial to include the special subtractive forms (900, 400, 90, 40, 9, 4) to ensure the greedy choice is always correct.
The algorithm iterates through these values. For each value, it checks if the remaining part of the number (num) is greater than or equal to it.
If it is, the corresponding symbol is appended to the result, and the value is subtracted from num. This repeats until num is smaller than the current value.
The process continues with the next smaller value until num becomes zero.
For example, to convert 1994:
- Start with
num = 1994. - Subtract 1000, append "M".
numbecomes 994. - Subtract 900, append "CM".
numbecomes 94. - Subtract 90, append "XC".
numbecomes 4. - Subtract 4, append "IV".
numbecomes 0. - The final result is "MCMXCIV".
class Solution { public String intToRoman(int num) { int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; StringBuilder roman = new StringBuilder(); for (int i = 0; i < values.length && num > 0; i++) { // Greedily append the largest possible symbol while (num >= values[i]) { num -= values[i]; roman.append(symbols[i]); } } return roman.toString(); }}Complexity
Time
O(1)
Space
O(1)
Trade-offs
Pros
Elegant and easy to understand.
More general and adaptable than hardcoding. If the numeral system were to change, only the mapping arrays would need to be updated.
Cons
Slightly less performant in practice than a direct lookup approach due to the looping structure, although both are O(1).
This approach leverages the fact that the input integer is constrained (1 to 3999) and that Roman numerals are constructed based on decimal place values. We can pre-calculate the Roman numeral representation for each possible digit (1-9) in each place (ones, tens, hundreds, thousands) and store them in arrays. The final Roman numeral is then constructed by looking up and concatenating the parts.
Algorithm
-
- Create four string arrays:
thousands,hundreds,tens, andones.
- Create four string arrays:
-
thousandsarray will store["", "M", "MM", "MMM"].
-
hundredsarray will store["", "C", ..., "CM"]for digits 0-9.
-
tensarray will store["", "X", ..., "XC"]for digits 0-9.
-
onesarray will store["", "I", ..., "IX"]for digits 0-9.
-
- Calculate the thousands part by looking up
thousands[num / 1000].
- Calculate the thousands part by looking up
-
- Calculate the hundreds part by looking up
hundreds[(num % 1000) / 100].
- Calculate the hundreds part by looking up
-
- Calculate the tens part by looking up
tens[(num % 100) / 10].
- Calculate the tens part by looking up
-
- Calculate the ones part by looking up
ones[num % 10].
- Calculate the ones part by looking up
-
- Concatenate the four parts and return the resulting string.
Walkthrough
We can break down the conversion into four independent parts: thousands, hundreds, tens, and ones.
For each place value, we create a lookup table (an array of strings) that maps a digit (0-9) to its Roman representation. For example, for the tens place, the digit 8 maps to "LXXX", and 9 maps to "XC".
The algorithm is as follows:
- Isolate the thousands digit (
num / 1000) and look up its Roman string from athousandsarray. - Isolate the hundreds digit (
(num % 1000) / 100) and look up its Roman string from ahundredsarray. - Isolate the tens digit (
(num % 100) / 10) and look up its Roman string from atensarray. - Isolate the ones digit (
num % 10) and look up its Roman string from aonesarray. - Concatenate these four strings to get the final result. For example, to convert 3749:
- Thousands:
3749 / 1000 = 3. Look upthousands[3]-> "MMM". - Hundreds:
(3749 % 1000) / 100 = 7. Look uphundreds[7]-> "DCC". - Tens:
(3749 % 100) / 10 = 4. Look uptens[4]-> "XL". - Ones:
3749 % 10 = 9. Look upones[9]-> "IX". - Result: "MMM" + "DCC" + "XL" + "IX" = "MMMDCCXLIX".
class Solution { public String intToRoman(int num) { String[] thousands = {"", "M", "MM", "MMM"}; String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; return thousands[num / 1000] + hundreds[(num % 1000) / 100] + tens[(num % 100) / 10] + ones[num % 10]; }}Complexity
Time
O(1)
Space
O(1)
Trade-offs
Pros
Extremely fast due to direct lookups and no loops.
Simple to implement once the lookup tables are defined.
Cons
Less flexible and not easily scalable. It's highly specific to the problem's constraints (1-3999).
The code is more verbose due to the explicit declaration of four arrays.
Solutions
Solution
public class Solution { public string IntToRoman ( int num ) { List < string > cs = new List < string >{ "M" , "CM" , "D" , "CD" , "C" , "XC" , "L" , "XL" , "X" , "IX" , "V" , "IV" , "I" }; List < int > vs = new List < int >{ 1000 , 900 , 500 , 400 , 100 , 90 , 50 , 40 , 10 , 9 , 5 , 4 , 1 }; StringBuilder ans = new StringBuilder (); for ( int i = 0 ; i < cs . Count ; i ++) { while ( num >= vs [ i ]) { ans . Append ( cs [ i ]); num -= vs [ i ]; } } return ans . ToString (); } }class Solution { public String intToRoman ( int num ) { List < String > cs = List . of ( "M" , "CM" , "D" , "CD" , "C" , "XC" , "L" , "XL" , "X" , "IX" , "V" , "IV" , "I" ); List < Integer > vs = List . of ( 1000 , 900 , 500 , 400 , 100 , 90 , 50 , 40 , 10 , 9 , 5 , 4 , 1 ); StringBuilder ans = new StringBuilder (); for ( int i = 0 , n = cs . size (); i < n ; ++ i ) { while ( num >= vs . get ( i )) { num -= vs . get ( i ); ans . append ( cs . get ( i )); } } return ans . toString (); } }class Solution { public: string intToRoman ( int num ) { vector < string > cs = { "M" , "CM" , "D" , "CD" , "C" , "XC" , "L" , "XL" , "X" , "IX" , "V" , "IV" , "I" }; vector < int > vs = { 1000 , 900 , 500 , 400 , 100 , 90 , 50 , 40 , 10 , 9 , 5 , 4 , 1 }; string ans ; for ( int i = 0 ; i < cs . size (); ++ i ) { while ( num >= vs [ i ]) { num -= vs [ i ]; ans += cs [ i ]; } } return ans ; } };class Solution : def intToRoman ( self , num : int ) -> str : cs = ( 'M' , 'CM' , 'D' , 'CD' , 'C' , 'XC' , 'L' , 'XL' , 'X' , 'IX' , 'V' , 'IV' , 'I' ) vs = ( 1000 , 900 , 500 , 400 , 100 , 90 , 50 , 40 , 10 , 9 , 5 , 4 , 1 ) ans = [] for c , v in zip ( cs , vs ): while num >= v : num -= v ans . append ( c ) return '' . join ( ans )Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.