Convert Roman Numerals to Integer
Problem and examples
Given a Roman numeral string, your task is to convert it into its corresponding integer value.
Roman numerals are represented by combinations of the following characters:
I= 1V= 5X= 10L= 50C= 100D= 500M= 1000
Some combinations follow a subtractive pattern, such as:
IV= 4IX= 9XL= 40XC= 90CD= 400CM= 900
Your job is to correctly interpret these symbols and return the total integer value. The input string is guaranteed to be a valid Roman numeral or an empty string.
| Input (Roman) | Output (Integer) | Description |
|---|---|---|
| "III" | 3 | Simple addition: 1 + 1 + 1 = 3 |
| "IV" | 4 | Subtractive case: 5 - 1 = 4 |
| "IX" | 9 | Subtractive case: 10 - 1 = 9 |
| "LVIII" | 58 | 50 + 5 + 1 + 1 + 1 = 58 |
| "MCMXCIV" | 1994 | 1000 + (1000 - 100) + (100 - 10) + (5 - 1) = 1994 |
| "X" | 10 | Single symbol: 10 |
| "" | 0 | Empty string returns 0 |
How it works
Understanding the Problem
We are given a string that represents a Roman numeral. Our task is to convert this Roman numeral into its integer equivalent.
To solve this, we need to understand two key things:
- Each Roman character maps to a fixed integer value (like
I = 1,V = 5,X = 10, etc.). - Sometimes, Roman numerals use a subtractive rule: a smaller numeral placed before a larger one means subtraction instead of addition (e.g.,
IV = 4).
Step-by-Step Solution with Example
Step 1: Map Roman characters to values
We define a dictionary that maps each Roman numeral to its integer value:
const romanMap = {
'I': 1, 'V': 5, 'X': 10,
'L': 50, 'C': 100,
'D': 500, 'M': 1000
};
Step 2: Process characters from left to right
We initialize a variable total to store the final result. We look at one character at a time and compare its value to the next one.
Step 3: Apply subtraction rule if needed
If the current character’s value is less than the next one, we subtract it. Otherwise, we add it.
Step 4: Walk through the example "MCMXCIV"
Let's break this down step-by-step for clarity:
M = 1000 → Add → total = 1000
C = 100, M = 1000 → C < M → Subtract → total = 1000 - 100 = 900
M = 1000 → Already processed with previous → skip
X = 10, C = 100 → X < C → Subtract → total = 900 - 10 = 890
C = 100 → Already processed with previous → skip
I = 1, V = 5 → I < V → Subtract → total = 890 - 1 = 889
V = 5 → Already processed with previous → skip
Add all remaining values: 1000 + 900 + 90 + 4 = 1994
Step 5: Return the final total
After looping through the string, return the total computed value.
Edge Cases
- Empty string: Return
0. There's nothing to process. - Single character: Just return its mapped value directly. E.g.,
"V"→ 5 - Invalid characters: Ideally, these should be caught and handled (e.g., with an error or by skipping them).
- Incorrect ordering: If subtractive notation is used incorrectly (like
IC), you may choose to return an error or ignore it depending on requirements.
Finally
This approach is easy to implement and intuitive once we understand how Roman numerals work. By processing the string from left to right and applying the subtractive rule carefully, we can accurately convert the numeral to its integer form in linear time, O(n).
It’s a perfect example of combining simple logic with careful attention to character relationships for accurate results.
- Create a map of Roman characters to their integer values.
- Initialize
result = 0. - Iterate over the string from left to right:
- → If current value is less than next value, subtract it.
- → Else, add it.
- Return the final result.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <string.h>
int value(char r) {
switch (r) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return 0;
}
}
int romanToInt(char *s) {
int total = 0, prev = 0;
for (int i = strlen(s) - 1; i >= 0; i--) {
int curr = value(s[i]);
if (curr < prev) total -= curr;
else total += curr;
prev = curr;
}
return total;
}
int main() {
printf("%d\n", romanToInt("MCMXCIV"));
return 0;
}C++ implementation
#include <iostream>
#include <unordered_map>
using namespace std;
int romanToInt(string s) {
unordered_map<char, int> map = {
{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50},
{'C', 100}, {'D', 500}, {'M', 1000}
};
int total = 0, prev = 0;
for (int i = s.size() - 1; i >= 0; --i) {
int curr = map[s[i]];
if (curr < prev) total -= curr;
else total += curr;
prev = curr;
}
return total;
}
int main() {
cout << romanToInt("MCMXCIV") << endl;
return 0;
}Python implementation
def roman_to_int(s):
roman_map = {'I': 1, 'V': 5, 'X': 10, 'L': 50,
'C': 100, 'D': 500, 'M': 1000}
total = 0
prev = 0
for ch in reversed(s):
curr = roman_map[ch]
if curr < prev:
total -= curr
else:
total += curr
prev = curr
return total
print(roman_to_int("MCMXCIV"))Java implementation
import java.util.*;
public class RomanToInteger {
public static int romanToInt(String s) {
Map<Character, Integer> map = Map.of(
'I', 1, 'V', 5, 'X', 10, 'L', 50,
'C', 100, 'D', 500, 'M', 1000
);
int total = 0, prev = 0;
for (int i = s.length() - 1; i >= 0; i--) {
int curr = map.get(s.charAt(i));
if (curr < prev) total -= curr;
else total += curr;
prev = curr;
}
return total;
}
public static void main(String[] args) {
System.out.println(romanToInt("MCMXCIV"));
}
}JavaScript implementation
function romanToInt(s) {
const map = {
'I': 1, 'V': 5, 'X': 10,
'L': 50, 'C': 100, 'D': 500, 'M': 1000
};
let total = 0, prev = 0;
for (let i = s.length - 1; i >= 0; i--) {
let curr = map[s[i]];
if (curr < prev) total -= curr;
else total += curr;
prev = curr;
}
return total;
}
console.log(romanToInt("MCMXCIV"));Go implementation
package main
import (
"fmt"
)
func romanToInt(s string) int {
romanMap := map[byte]int{
'I': 1, 'V': 5, 'X': 10, 'L': 50,
'C': 100, 'D': 500, 'M': 1000,
}
total, prev := 0, 0
for i := len(s) - 1; i >= 0; i-- {
curr := romanMap[s[i]]
if curr < prev {
total -= curr
} else {
total += curr
}
prev = curr
}
return total
}
func main() {
fmt.Println(romanToInt("MCMXCIV"))
}Rust implementation
use std::collections::HashMap;
fn roman_to_int(s: &str) -> i32 {
let map: HashMap<char, i32> = [('I',1),('V',5),('X',10),('L',50),
('C',100),('D',500),('M',1000)].iter().cloned().collect();
let mut total = 0;
let mut prev = 0;
for c in s.chars().rev() {
let curr = *map.get(&c).unwrap();
if curr < prev {
total -= curr;
} else {
total += curr;
}
prev = curr;
}
total
}
fn main() {
println!("{}", roman_to_int("MCMXCIV"));
}Kotlin implementation
fun romanToInt(s: String): Int {
val map = mapOf(
'I' to 1, 'V' to 5, 'X' to 10, 'L' to 50,
'C' to 100, 'D' to 500, 'M' to 1000
)
var total = 0
var prev = 0
for (ch in s.reversed()) {
val curr = map[ch]!!
if (curr < prev) total -= curr else total += curr
prev = curr
}
return total
}
fun main() {
println(romanToInt("MCMXCIV"))
}Swift implementation
func romanToInt(_ s: String) -> Int {
let map: [Character: Int] = [
"I": 1, "V": 5, "X": 10, "L": 50,
"C": 100, "D": 500, "M": 1000
]
var total = 0, prev = 0
for ch in s.reversed() {
let curr = map[ch]!
if curr < prev {
total -= curr
} else {
total += curr
}
prev = curr
}
return total
}
print(romanToInt("MCMXCIV"))TypeScript implementation
function romanToInt(s: string): number {
const map: { [key: string]: number } = {
'I': 1, 'V': 5, 'X': 10,
'L': 50, 'C': 100, 'D': 500, 'M': 1000
};
let total = 0, prev = 0;
for (let i = s.length - 1; i >= 0; i--) {
let curr = map[s[i]];
if (curr < prev) total -= curr;
else total += curr;
prev = curr;
}
return total;
}
console.log(romanToInt("MCMXCIV"));Time complexity
| Case | Time | Explanation |
|---|---|---|
| best case | O(n) | The algorithm goes through the Roman numeral string once, where n is its length. |
| average case | O(n) | In all scenarios, every character is processed once. |
| worst case | O(n) | No matter the combination, we always loop through all characters. |
Space complexity
O(1)
Only constant space is used to store the result and the mapping of characters.