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
):IV
and 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 places
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
Constraints:
1 <= num <= 3999
Problem Description
In this problem, we are given the task to convert an integer to its equivalent Roman numeral representation. Roman numerals use a specific set of symbols that correspond to certain values. The seven symbols used are I, V, X, L, C, D, and M. Here’s their corresponding values:
- I represents 1
- V represents 5
- X represents 10
- L represents 50
- C represents 100
- D represents 500
- M represents 1000
Roman numerals are written by combining these symbols starting from the largest to the smallest from left to right. However, instead of repeating a symbol four times, Roman numerals use a subtractive notation for certain numbers. For instance, the number 4 isn’t represented as “IIII” but as “IV” (1 before 5 signifies subtraction, giving us 4). This rule of subtraction applies to several pairs, such as I before V or X, X before L or C, and C before D or M. The problem requires us to implement the logic that can convert any given integer within the permissible range into this Roman numeral format.
Intuition
The solution approach is a greedy one. We work through the integer from the largest possible Roman numeral to the smallest. We start by creating an ordered list of Roman numeral characters (cs
) and their respective integer values (vs
), including those representing the subtraction cases. For example, “CM” for 900, “XL” for 40 and so on. These need to be in descending order so that we can start with the largest possible values and work our way down.
We initialize an empty list ans
that will be used to store the Roman numeral symbols sequentially as we build up our answer. Now, for each pair (symbol and its value) we check if our integer (num
) is greater than or equal to the current value (v
). If it is, we subtract this value from num
and append the corresponding Roman numeral symbol to our ans
list. We repeat this process, checking and subtracting the same value, until num
is smaller than the current value. Then we proceed to the next value-symbol pair and continue the process.
Essentially, we are always trying to subtract the largest possible values from our given integer, which is why this method is considered greedy. This continues until our integer (num
) has been reduced to 0, meaning we’ve found a Roman numeral representation for each part of the initial number. Finally, we concatenate the symbols in our ans
list and return the result as the Roman numeral representation of the initial integer.
Solution Approach
The solution to this problem employs a simple but efficient algorithm using Greedy approach, array manipulation, and sequential iteration. The key data structures used are tuples for storing Roman numeral characters and their corresponding values, and a list to construct the final Roman numeral string.
Here’s an in-depth look at each step of the algorithm:
- Define Symbol-Value Pairs: Two tuples are created, one holding the Roman numeral characters
cs
and the other holding their corresponding valuesvs
. These are aligned by indices and include entries for the subtractive combinations like “CM” for 900, and “IV” for 4, among others. These tuples are ordered from the largest value to the smallest. - Initialize the Result Holder: An empty list,
ans
, is created to hold the characters that will eventually form the final Roman numeral string. - Iterate Through Symbol-Value Pairs: We iterate through the pairs in the order they are defined, starting with the largest value.
- Greedy Subtraction and Accumulation: In each iteration, we check if our current number
num
is greater than or equal to the valuev
at the current iteration. If it is, this means we can represent a part of our number with the Roman numeral symbolc
. We subtract this valuev
fromnum
and append the symbolc
to ourans
list. - Repeat Subtractions: We continue to subtract and append the same symbol until
num
is less thanv
. This process follows the Greedy approach because it always consumes the largest possible “chunk” of the number in each step, hence reducing the number in the largest steps possible. - Proceed To Next Symbol-Value Pair: Once
num
is smaller than the current valuev
, we move on to the next pair. This essentially transitions to the next largest Roman symbol thatnum
can accommodate. - Concatenate the Result: After the loop exits, we combine all characters in the
ans
list into a string, which gives us the final Roman numeral.
The pseudocode makes this process clear:
function intToRoman(num):
initialize cs as tuples ("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I")
initialize vs as tuples (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
initialize ans as an empty list
for each (c, v) in zip(cs, vs):
while num >= v:
subtract v from num
append c to ans
return the concatenation of items in ans
In essence, this algorithm is a straightforward application of the Greedy algorithm, suitable for the problem’s requirements, as the representation of Roman numerals is inherently “Greedy” — favoring the usage of larger denominations whenever possible. The use of tuples and list operations makes the implementation concise and efficient.
Example Walkthrough
Let’s go through a specific example using the integer 1437 to illustrate the solution approach described earlier. Here’s how the algorithm would convert the number 1437 into a Roman numeral:
- Start With the Largest Symbol-Value Pair: The first value and symbol pair in our pre-defined tuples that 1437 is greater than or equal to is “M” which corresponds to 1000.
- Subtract and Accumulate: We subtract 1000 from 1437, leaving us with 437, and add “M” to our
ans
list. - Repeat with the Remaining Number: Looking at the next largest symbol-value pair, no value is greater than or equal to 437 until we get to “CD” for 400.
- Subtract and Accumulate: Subtract 400 from 437, getting 37, and add “CD” to our
ans
list. - Continue the Process: There’s no symbol for 37, so we look for the next highest which is “X” corresponding to 10.
- Subtract and Accumulate: We can subtract 10 from 37 three times, adding “X” to our
ans
list three times, and are left with 7. - Finish Up: The highest value for 7 is “V” which is 5. We subtract 5 from 7 and add “V” to our
ans
list, leaving us with 2. - Final Steps: We can subtract 1 from 2 twice, so we add “I” to our list twice.
- Concatenate the Result: Now that our number has been reduced to 0, we concatenate everything we’ve added to our
ans
list and end up with “MCDXXXVII” as the Roman numeral representation of 1437.
So the step-by-step process for the number 1437 goes like this:
- Start at 1437, subtract 1000, list is now [“M”].
- 437 remains, subtract 400, list is now [“M”, “CD”].
- 37 remains, subtract 30 (3 * 10), list is now [“M”, “CD”, “X”, “X”, “X”].
- 7 remains, subtract 5, list is now [“M”, “CD”, “X”, “X”, “X”, “V”].
- 2 remains, subtract 2 (2 * 1), list is now [“M”, “CD”, “X”, “X”, “X”, “V”, “I”, “I”].
- Concatenate list items to get “MCDXXXVII”.
This example captures the essence of the greedy algorithm in converting an integer to a Roman numeral, consistently taking the largest possible value that fits into what remains of the number until the entire number is converted.
Solution Implementation
class Solution {
public:
string intToRoman(int num) {
// Define pairs of integer values and their corresponding Roman numeral symbols
const vector<pair<int, string>> valueSymbols{
{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"}}; // List of Roman numeral symbols in descending order
string res; // Initialize an empty string to store the result
// Loop through each value-symbol pair in the valueSymbols list
for (const auto& [value, symbol] : valueSymbols) {
if (num == 0) // If the number is reduced to zero, break out of the loop
break;
// While the current value can fit into the number, append the corresponding symbol
while (num >= value) {
res += symbol; // Append the Roman symbol to the result string
num -= value; // Subtract the value from the number
}
}
return res; // Return the final Roman numeral as a string
}
};
class Solution {
public String intToRoman(int num) {
// Array of values for Roman numerals from largest to smallest
final int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
// Array of corresponding Roman symbols for the values in 'values' array
final String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
StringBuilder sb = new StringBuilder(); // StringBuilder to efficiently append the result Roman numerals
// Loop through each value-symbol pair in the values and symbols arrays
for (int i = 0; i < values.length; ++i) {
if (num == 0) // If the number is reduced to zero, break out of the loop
break;
// While the current value can fit into the number, append the corresponding symbol
while (num >= values[i]) {
sb.append(symbols[i]); // Append the Roman symbol
num -= values[i]; // Subtract the value from the number
}
}
return sb.toString(); // Return the final Roman numeral as a string
}
}
class Solution:
def intToRoman(self, num: int) -> str:
# List of tuples containing the Roman numeral values and their corresponding symbols
value_symbols = [
(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')
]
res = [] # List to accumulate the Roman numeral symbols
# Iterate over each value-symbol pair in the value_symbols list
for value, symbol in value_symbols:
if num == 0: # If the number has been reduced to 0, break the loop
break
count = num // value # Determine how many times the current value fits into the number
res.append(symbol * count) # Append the corresponding symbol 'count' times to the result list
num -= count * value # Subtract the value from the number
return ''.join(res) # Join the symbols in the result list into a single string and return it
char* intToRoman(int num) {
// Allocate enough memory for the result string
// Max length of Roman numeral for numbers up to 3999 is 15 (e.g., "MMMCMXCIX")
char* s = (char*)malloc(20 * sizeof(char));
int i = 0;
// Handle the largest values first (greedy approach)
while (num >= 1000) {
s[i++] = 'M';
num -= 1000;
}
// Check for 900 (CM)
if (num >= 900) {
s[i++] = 'C';
s[i++] = 'M';
num -= 900;
}
// Handle 500 (D)
while (num >= 500) {
s[i++] = 'D';
num -= 500;
}
// Check for 400 (CD)
if (num >= 400) {
s[i++] = 'C';
s[i++] = 'D';
num -= 400;
}
// Handle 100 (C)
while (num >= 100) {
s[i++] = 'C';
num -= 100;
}
// Check for 90 (XC)
if (num >= 90) {
s[i++] = 'X';
s[i++] = 'C';
num -= 90;
}
// Handle 50 (L)
while (num >= 50) {
s[i++] = 'L';
num -= 50;
}
// Check for 40 (XL)
if (num >= 40) {
s[i++] = 'X';
s[i++] = 'L';
num -= 40;
}
// Handle 10 (X)
while (num >= 10) {
s[i++] = 'X';
num -= 10;
}
// Check for 9 (IX)
if (num >= 9) {
s[i++] = 'I';
s[i++] = 'X';
num -= 9;
}
// Handle 5 (V)
while (num >= 5) {
s[i++] = 'V';
num -= 5;
}
// Check for 4 (IV)
if (num >= 4) {
s[i++] = 'I';
s[i++] = 'V';
num -= 4;
}
// Handle 1 (I)
while (num >= 1) {
s[i++] = 'I';
num -= 1;
}
// Null-terminate the string
s[i] = '\0';
// Return the constructed Roman numeral string
return s;
}
public class Solution {
// 2D array to represent the Roman numeral digits for each place (ones, tens, hundreds, thousands)
// Each row holds the basic Roman symbols for a given place value (e.g., 'I', 'V' for ones)
private readonly char[,] RomanDigits =
{
{ 'I', 'V' }, // Roman digits for ones (1-9)
{ 'X', 'L' }, // Roman digits for tens (10-90)
{ 'C', 'D' }, // Roman digits for hundreds (100-900)
{ 'M', '\0' } // Roman digits for thousands (1000+), '\0' signifies there's no next symbol
};
// Function to convert an integer to a Roman numeral
public string IntToRoman(int num)
{
string result = string.Empty; // Initialize the result string to store the Roman numeral
int lastDigit; // Variable to store the last digit of the number
int power = 0; // Variable to track the place value (ones, tens, hundreds, etc.)
// Loop until all digits of the number are processed
while (num > 0)
{
lastDigit = num % 10; // Extract the last digit of the number
result = ConvertDigitToRoman(lastDigit, power) + result; // Convert digit to Roman numeral and prepend to the result
num /= 10; // Remove the last digit of the number
power++; // Move to the next place value (e.g., tens, hundreds, etc.)
}
return result; // Return the final Roman numeral string
}
// Helper function to convert a single digit to Roman numerals based on its place value (ones, tens, hundreds, etc.)
private string ConvertDigitToRoman(int digit, int power)
{
switch (digit)
{
// Case for digits less than 4 (e.g., 1, 2, 3)
case < 4:
return string.Empty.PadLeft(digit, RomanDigits[power, 0]); // Repeat the Roman numeral symbol for the digit value
// Case for digit equal to 4 (e.g., 4)
case 4:
return string.Concat(RomanDigits[power, 0], RomanDigits[power, 1]); // Combine "I" and "V" for 4
// Case for digit equal to 5 (e.g., 5)
case 5:
return RomanDigits[power, 1].ToString(); // Return the Roman numeral for 5 (e.g., "V")
// Case for digits less than 9 but greater than or equal to 6 (e.g., 6, 7, 8)
case < 9:
return RomanDigits[power, 1].ToString().PadRight(digit - 5 + 1, RomanDigits[power, 0]); // Combine "V" with repeated "I" for 6-8
// Case for digit equal to 9 (e.g., 9)
case 9:
if (power < 3)
return string.Concat(RomanDigits[power, 0], RomanDigits[power + 1, 0]); // Combine "I" and "X" for 9 in ones/tens
else
return string.Empty; // For thousands, no symbol for 9 (as per Roman numeral rules)
// Default case for invalid input (should not occur)
default:
return string.Empty;
}
}
}
var intToRoman = function(num) {
// Array of pairs: value and its corresponding Roman numeral symbol
const valueSymbols = [
[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']
];
let res = ''; // Initialize the result string to store the Roman numeral
// Iterate over the value-symbol pairs
for (const [value, symbol] of valueSymbols) {
if (num === 0) break; // If num becomes 0, exit the loop
const count = Math.floor(num / value); // Determine how many times the value fits in num
res += symbol.repeat(count); // Append the symbol 'count' times to the result string
num -= count * value; // Subtract the corresponding value from num
}
return res; // Return the final Roman numeral string
};
function intToRoman(num: number): string {
// Array of values corresponding to Roman numerals from largest to smallest
const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
// Array of Roman numeral symbols corresponding to the values
const symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
let result = ""; // Initialize the result string to store the Roman numeral
// Iterate over the values array and build the Roman numeral
for (let i = 0; i < values.length; i++) {
// While the number is greater than or equal to the current value
while (num >= values[i]) {
result += symbols[i]; // Append the corresponding Roman numeral symbol
num -= values[i]; // Subtract the value from the number
}
}
return result; // Return the final Roman numeral string
}
class Solution {
/**
* Function to convert an integer to a Roman numeral
* @param Integer $num The integer to be converted
* @return String The Roman numeral representation of the integer
*/
function intToRoman($num) {
$outString = ''; // Initialize the result string to store the Roman numeral
// Mapping of values to Roman numeral symbols, from largest to smallest
$map = array(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');
$numString = (string) $num; // Convert the number to a string (not used in logic here)
// Iterate through the map and build the Roman numeral string
foreach ($map as $number => $letter) {
// While the number is larger than or equal to the current value in the map
while ($num >= $number) {
$outString .= $letter; // Append the corresponding Roman numeral symbol
$num -= $number; // Subtract the value from the number
}
}
return $outString; // Return the final Roman numeral string
}
}
class Solution {
// Array of values corresponding to the Roman numeral symbols, from largest to smallest
private let values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
// Array of Roman numeral symbols corresponding to the values
private let symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
// Function to convert an integer to its Roman numeral representation
func intToRoman(_ num: Int) -> String {
var int = num // The integer to be converted
var sym = "" // String to accumulate the Roman numeral symbols
// Loop while the number is greater than 0
while int > 0 {
// Iterate over the values and symbols
for (i, d) in values.enumerated() where int - d >= 0 {
int -= d // Subtract the value from the integer
sym += symbols[i] // Append the corresponding Roman numeral symbol
break // Break after finding the largest applicable symbol
}
}
return sym // Return the resulting Roman numeral string
}
}
// Array of symbol-value pairs for Roman numerals, from largest to smallest
val symbols = arrayOf(
1000 to "M", // 1000 maps to "M"
900 to "CM", // 900 maps to "CM"
500 to "D", // 500 maps to "D"
400 to "CD", // 400 maps to "CD"
100 to "C", // 100 maps to "C"
90 to "XC", // 90 maps to "XC"
50 to "L", // 50 maps to "L"
40 to "XL", // 40 maps to "XL"
10 to "X", // 10 maps to "X"
9 to "IX", // 9 maps to "IX"
5 to "V", // 5 maps to "V"
4 to "IV", // 4 maps to "IV"
1 to "I", // 1 maps to "I"
)
// Precompute the Roman numerals for numbers 0 to 3999 and store in an array
@JvmField
val numerals = Array(4000) { intToRoman(it) }
// Function to convert an integer to its Roman numeral representation
fun intToRoman(num: Int): String = buildString {
// Use fold to iterate through each symbol and subtract the corresponding value from the number
symbols.fold(num) { acc, (n, str) ->
val times = acc / n // Calculate how many times the symbol can be repeated
for (i in 0 until times) append(str) // Append the symbol the appropriate number of times
acc - n * times // Subtract the value from the current number
}
}
// Extension function to convert an integer to Roman numeral using precomputed values
inline fun Any?.intToRoman(num: Int): String = numerals[num]
// Inline function returning 0, possibly placeholder for a solution
inline fun Solution(): Int = 0
class Solution {
String intToRoman(int num) {
// Arrays holding the Roman numerals for ones, tens, hundreds, and thousands
final ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];
final tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"];
final hunds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"];
final thous = ["", "M", "MM", "MMM", "MMMM"];
// Get the Roman numeral for thousands, hundreds, tens, and ones based on the input number
final t = thous[num ~/ 1000]; // Get the Roman numeral for the thousands place
final h = hunds[num ~/ 100 % 10]; // Get the Roman numeral for the hundreds place
final te = tens[num ~/ 10 % 10]; // Get the Roman numeral for the tens place
final o = ones[num % 10]; // Get the Roman numeral for the ones place
// Combine the results for thousands, hundreds, tens, and ones to form the Roman numeral
return t + h + te + o;
}
}
func intToRoman(num int) string {
return getRoman(num, "") // Call the helper function to convert the integer to Roman numeral
}
func getRoman(num int, roman string) string {
if num == 0 {
return roman // Base case: when num is 0, return the accumulated Roman numeral
}
// Check for each Roman numeral starting from the largest value and subtract it from num
if num - 1000 >= 0 {
return getRoman(num-1000, roman + "M") // Subtract 1000 and add "M" to the Roman numeral
}
if num - 900 >= 0 {
return getRoman(num-900, roman + "CM") // Subtract 900 and add "CM"
}
if num - 500 >= 0 {
return getRoman(num-500, roman + "D") // Subtract 500 and add "D"
}
if num - 400 >= 0 {
return getRoman(num-400, roman + "CD") // Subtract 400 and add "CD"
}
if num - 100 >= 0 {
return getRoman(num-100, roman + "C") // Subtract 100 and add "C"
}
if num - 90 >= 0 {
return getRoman(num-90, roman + "XC") // Subtract 90 and add "XC"
}
if num - 50 >= 0 {
return getRoman(num-50, roman + "L") // Subtract 50 and add "L"
}
if num - 40 >= 0 {
return getRoman(num-40, roman + "XL") // Subtract 40 and add "XL"
}
if num - 10 >= 0 {
return getRoman(num-10, roman + "X") // Subtract 10 and add "X"
}
if num - 9 >= 0 {
return getRoman(num-9, roman + "IX") // Subtract 9 and add "IX"
}
if num - 5 >= 0 {
return getRoman(num-5, roman + "V") // Subtract 5 and add "V"
}
if num - 4 >= 0 {
return getRoman(num-4, roman + "IV") // Subtract 4 and add "IV"
}
if num - 1 >= 0 {
return getRoman(num-1, roman + "I") // Subtract 1 and add "I"
}
return roman // Return the final Roman numeral string
}
Time and Space Complexity
The time complexity of the given Python function intToRoman
is O(1), because the number of operations performed does not depend on the size of the input number but on the fixed set of Roman numeral symbols. Since Roman numerals have a finite number of symbols (cs
) that are considered to provide the greatest value in the Roman numeral system, the loop within the function will iterate at most a constant number of times, which is equivalent to the number of symbols in the set cs
(13 symbols in this case).
The space complexity of the function is also O(1) for similar reasons. The amount of memory used by the program does not increase proportionally to the input num
. It rather depends on the size of the two constant arrays cs
and vs
, and the list ans
. Regardless of the size of num
, ans
will have at most a constant number of elements related to the number of possible Roman numeral symbols. Therefore, the space used by the list ans
will not grow arbitrarily with the input value.
To summarize, both the time complexity and the space complexity of the function are constant, represented as:
Time Complexity: O(1)
Space Complexity: O(1)