← Back to DevBytes

Roman to Integer: Multiple Solutions and Complexity Analysis

Introduction to Roman to Integer Conversion

Converting Roman numerals to integers is a classic algorithmic problem that frequently appears in software engineering interviews and competitive programming. Roman numerals are a numeral system originating in ancient Rome, using combinations of letters from the Latin alphabet to signify values. While they are no longer used for widespread day-to-day calculations, they still appear in modern contexts such as book chapter numbering, movie release years, and clock faces.

What is Roman to Integer Conversion?

The process involves taking a string of Roman numeral characters (e.g., "MCMXCIV") and translating it into its corresponding integer value (e.g., 1994). This requires understanding the specific values assigned to each Roman symbol and the subtractive notation rules that govern their combinations.

Why Does it Matter?

Beyond interview preparation, parsing Roman numerals is a practical exercise in string manipulation, hash mapping, and conditional logic. It teaches developers how to handle stateful iteration—where the meaning of a current character depends on the character that follows or precedes it. Understanding these conversion algorithms helps build a foundation for writing custom parsers and interpreters.

Understanding the Roman Numeral System

Before diving into the code, it is crucial to establish the mapping of Roman symbols to their integer values:

Generally, Roman numerals are written in descending order of value from left to right. However, to avoid four identical consecutive characters (like IIII for 4), subtractive notation is used. In subtractive notation, a smaller numeral placed before a larger numeral indicates subtraction. For example, IV is 4 (5 - 1), IX is 9 (10 - 1), and CD is 400 (500 - 100).

Solution 1: The Forward Iteration Approach

The most intuitive way to solve this problem is to iterate through the string from left to right. For each character, we compare its value to the value of the next character. If the current value is less than the next value, we have encountered a subtractive pair, so we subtract the current value. Otherwise, we simply add the current value.

How it Works

Implementation in Python

def roman_to_integer_forward(s: str) -> int:
    roman_map = {
        'I': 1, 'V': 5, 'X': 10, 'L': 50,
        'C': 100, 'D': 500, 'M': 1000
    }
    
    total = 0
    n = len(s)
    
    for i in range(n - 1):
        current_val = roman_map[s[i]]
        next_val = roman_map[s[i + 1]]
        
        if current_val < next_val:
            total -= current_val
        else:
            total += current_val
            
    # Add the last character
    total += roman_map[s[-1]]
    
    return total

# Example usage:
print(roman_to_integer_forward("MCMXCIV"))  # Output: 1994

Complexity Analysis

Time Complexity: O(n), where n is the length of the string. We iterate through the string exactly once.

Space Complexity: O(1). The dictionary has a fixed size of 7 entries, and we only use a few integer variables, so the auxiliary space is constant regardless of the input size.

Solution 2: The Reverse Iteration Approach

An alternative approach is to iterate through the string in reverse (from right to left). This method mimics how we naturally read Roman numerals to calculate their value, keeping track of the largest numeral seen so far. If we encounter a numeral smaller than the largest one seen, it means we are in a subtractive situation.

How it Works

Implementation in JavaScript

function romanToIntegerReverse(s) {
    const romanMap = {
        'I': 1, 'V': 5, 'X': 10, 'L': 50,
        'C': 100, 'D': 500, 'M': 1000
    };
    
    let total = 0;
    let prevValue = 0;
    
    for (let i = s.length - 1; i >= 0; i--) {
        let currentValue = romanMap[s[i]];
        
        if (currentValue < prevValue) {
            total -= currentValue;
        } else {
            total += currentValue;
        }
        
        prevValue = currentValue;
    }
    
    return total;
}

// Example usage:
console.log(romanToIntegerReverse("LVIII"));  // Output: 58

Complexity Analysis

Time Complexity: O(n). The algorithm processes each character of the string exactly one time in reverse order.

Space Complexity: O(1). The space used by the hash map and variables remains constant.

Solution 3: The String Replacement Method

While not the most efficient, a creative approach to this problem involves eliminating subtractive notation entirely before summing the values. By replacing the special cases (IV, IX, XL, XC, CD, CM) with their additive equivalents (e.g., IV becomes IIII, IX becomes VIIII), we can simply sum the individual characters without needing to look ahead or look behind.

How it Works

Implementation in Python

def roman_to_integer_replace(s: str) -> int:
    roman_map = {
        'I': 1, 'V': 5, 'X': 10, 'L': 50,
        'C': 100, 'D': 500, 'M': 1000
    }
    
    # Eliminate subtractive notation by replacing it with additive
    s = s.replace("IV", "IIII").replace("IX", "VIIII")
    s = s.replace("XL", "XXXX").replace("XC", "VXXXX")
    s = s.replace("CD", "CCCC").replace("CM", "DCCCC")
    
    total = 0
    for char in s:
        total += roman_map[char]
        
    return total

# Example usage:
print(roman_to_integer_replace("IX"))  # Output: 9

Complexity Analysis

Time Complexity: O(n). Although there are multiple replace operations, the string length remains relatively small, and string replacement in most languages is linear with respect to the string length. The final summation loop is also O(n).

Space Complexity: O(n). In the worst-case scenario, the string replacement operations can create a new string that is longer than the original (e.g., "IX" becomes "VIIII", expanding from 2 characters to 5). Thus, the space complexity scales with the size of the modified string.

Best Practices

When implementing Roman to integer conversion in production code or interviews, keep the following best practices in mind:

Conclusion

Converting Roman numerals to integers is an excellent exercise in algorithm design that highlights the importance of string traversal and state management. While multiple solutions exist—ranging from forward and reverse iteration to string manipulation—the forward and reverse iterative approaches stand out for their optimal O(n) time and O(1) space complexity. By understanding the underlying rules of subtractive notation and applying a simple hash map, you can efficiently parse these ancient symbols into modern integers, equipping yourself with a robust tool for both practical parsing tasks and technical interviews.

— Ad —

Google AdSense will appear here after approval

← Back to all articles