StringProblem 26 of 43

Converting Roman Numerals to Decimal

Brute Force
Time: O(n)
Space: O(1)
Optimal
Time: O(n)
Space: O(1)

Problem Statement

Given a Roman numeral string, convert it to its decimal (integer) equivalent.

Roman Numeral Values:

  • I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000

Special Rules (Subtraction):

  • I before V (5) or X (10) → subtract I (e.g., IV = 4, IX = 9)
  • X before L (50) or C (100) → subtract X (e.g., XL = 40, XC = 90)
  • C before D (500) or M (1000) → subtract C (e.g., CD = 400, CM = 900)

Example:

  • Input: "MCMXCIV"
  • Output: 1994
  • Explanation: M=1000, CM=900, XC=90, IV=4 → 1000+900+90+4 = 1994

Approach 1: Check Each Pair

Intuition

Process two characters at a time. If the current pair forms a subtraction pattern (like IV, IX), handle it specially; otherwise, add the current character's value.

Algorithm

  1. Create a mapping of Roman numerals to values
  2. Create a mapping for two-character subtraction patterns
  3. Iterate through the string
  4. If current position forms a two-char pattern, add its value and skip next
  5. Otherwise, add single character value
  6. Return total sum
java
import java.util.*;

public class Solution {
    public int romanToDecimal(String s) {
        Map<Character, Integer> single = new HashMap<>();
        single.put('I', 1); single.put('V', 5);
        single.put('X', 10); single.put('L', 50);
        single.put('C', 100); single.put('D', 500);
        single.put('M', 1000);
        
        Map<String, Integer> pairs = new HashMap<>();
        pairs.put("IV", 4); pairs.put("IX", 9);
        pairs.put("XL", 40); pairs.put("XC", 90);
        pairs.put("CD", 400); pairs.put("CM", 900);
        
        int result = 0;
        int i = 0;
        int n = s.length();
        
        while (i < n) {
            // Check for two-character pattern
            if (i + 1 < n) {
                String twoChar = s.substring(i, i + 2);
                if (pairs.containsKey(twoChar)) {
                    result += pairs.get(twoChar);
                    i += 2;
                    continue;
                }
            }
            
            // Single character
            result += single.get(s.charAt(i));
            i++;
        }
        
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(n) - Single pass through the string
  • Space Complexity: O(1) - Fixed-size maps

Approach 2: Compare with Next (Optimal)

Intuition

If a smaller value appears before a larger value, we subtract it; otherwise, we add it. This eliminates the need for a separate pairs map.

Algorithm

  1. Create mapping of Roman numerals to values
  2. Iterate through the string
  3. If current value < next value, subtract current from result
  4. Otherwise, add current to result
  5. Return final result
java
import java.util.*;

public class Solution {
    public int romanToDecimal(String s) {
        Map<Character, Integer> values = new HashMap<>();
        values.put('I', 1); values.put('V', 5);
        values.put('X', 10); values.put('L', 50);
        values.put('C', 100); values.put('D', 500);
        values.put('M', 1000);
        
        int result = 0;
        int n = s.length();
        
        for (int i = 0; i < n; i++) {
            int current = values.get(s.charAt(i));
            int next = (i + 1 < n) ? values.get(s.charAt(i + 1)) : 0;
            
            if (current < next) {
                result -= current;  // Subtraction case
            } else {
                result += current;  // Addition case
            }
        }
        
        return result;
    }
}

Dry Run Example

Input: "MCMXCIV" Position 0: M(1000), next=C(100), 1000 > 100, ADD 1000, result = 1000 Position 1: C(100), next=M(1000), 100 < 1000, SUBTRACT 100, result = 900 Position 2: M(1000), next=X(10), 1000 > 10, ADD 1000, result = 1900 Position 3: X(10), next=C(100), 10 < 100, SUBTRACT 10, result = 1890 Position 4: C(100), next=I(1), 100 > 1, ADD 100, result = 1990 Position 5: I(1), next=V(5), 1 < 5, SUBTRACT 1, result = 1989 Position 6: V(5), next=0, 5 > 0, ADD 5, result = 1994 Output: 1994

Complexity Analysis

  • Time Complexity: O(n) - Single pass
  • Space Complexity: O(1) - Fixed-size map

Approach 3: Right to Left Traversal

Intuition

Process from right to left. If current value is less than the previous maximum value seen, subtract it; otherwise, add it and update max.

java
import java.util.*;

public class Solution {
    public int romanToDecimalRTL(String s) {
        Map<Character, Integer> values = new HashMap<>();
        values.put('I', 1); values.put('V', 5);
        values.put('X', 10); values.put('L', 50);
        values.put('C', 100); values.put('D', 500);
        values.put('M', 1000);
        
        int result = 0;
        int maxSoFar = 0;
        
        // Process right to left
        for (int i = s.length() - 1; i >= 0; i--) {
            int current = values.get(s.charAt(i));
            
            if (current < maxSoFar) {
                result -= current;
            } else {
                result += current;
                maxSoFar = current;
            }
        }
        
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(n)
  • Space Complexity: O(1)

Key Takeaways

  1. Pattern recognition: Subtraction happens when a smaller numeral precedes a larger one
  2. Compare with next is cleaner than checking all special cases explicitly
  3. Right-to-left traversal with max tracking is an elegant alternative
  4. All approaches are O(n) - the difference is in code clarity
  5. This is a common interview question testing attention to edge cases
  6. The reverse problem (integer to Roman) is also frequently asked