Problem
Given a Roman numeral string, return its integer value. Roman numerals use seven symbols (I=1, V=5, X=10, L=50, C=100, D=500, M=1000), with one twist: when a smaller value appears immediately before a larger one, it's subtracted rather than added โ so IV=4 and CM=900.
-
Input:
s = "MCMXCIV" -
Output:
1994 -
Explanation: M(1000) + CM(900) + XC(90) + IV(4) = 1994
-
Input:
s = "LVIII" -
Output:
58 -
Explanation: L(50) + V(5) + I(1) + I(1) + I(1) = 58
Intuition
The subtraction rule only ever applies when a smaller symbol immediately precedes a larger one. So one left-to-right pass is enough: look one step ahead at each position, and if the current symbol is worth less than the next one, subtract it instead of adding it. No special-casing needed for IV, IX, XL, etc. โ the comparison handles all six subtraction pairs automatically.
Solution โ Left-to-Right Comparison
Scan the string once, comparing each character's value to the next character's value to decide whether to add or subtract.
- Build a lookup map of the 7 Roman symbols to their integer values.
- Iterate through the string from left to right.
- At each position, look up the current symbol's value and (if it exists) the next symbol's value.
- If the current value is less than the next value, subtract the current value from the total (it's part of a subtraction pair).
- Otherwise, add the current value to the total.
- Return the total.
1def romanToInt(s: str) -> int:
2 roman_values = {'I': 1, 'V': 5, 'X': 10, 'L': 50,
3 'C': 100, 'D': 500, 'M': 1000}
4 total = 0
5 for i in range(len(s)):
6 current_value = roman_values[s[i]]
7 next_value = roman_values[s[i + 1]] if i + 1 < len(s) else 0
8 # subtract when a smaller symbol comes before a larger one
9 if current_value < next_value:
10 total -= current_value
11 else:
12 total += current_value
13 return totalTime: O(n) โ single pass through the string of length n.
Space: O(1) โ the symbol map has exactly 7 entries regardless of input size.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Left-to-Right Comparison | O(n) | O(1) | Always โ this is the canonical solution |
Common Mistakes
- Hardcoding all six subtraction pairs explicitly (checking
s[i:i+2] == "IV", etc.) instead of using the general comparison rule โ it works but produces six times as much code and misses the elegant insight. - Checking
s[i] < s[i+1]without guarding against the last index โ the final character has no successor, so you must either guard with a bounds check or default the "next value" to 0. - Scanning right to left without adjusting the comparison direction โ a right-to-left scan also works (track the previous value and subtract if current is smaller), but mixing up the direction of the comparison flips all the add/subtract decisions.
- Forgetting that the same symbol can appear in both additive and subtractive roles โ I is added in "III" but subtracted in "IX"; only the relative order with the next character determines which.
- Using string replacement (e.g., replacing "IV" with "IIII" before summing) โ technically correct but obscures the pattern and is error-prone for longer subtraction sequences.
Related Problems
integer-to-romanโ the exact inverse: build a Roman numeral from an integer using the same symbol tableadd-binaryโ another problem where you process a string representation of a number one character at a timemultiply-stringsโ digit-by-digit string arithmetic using a similar positional scanning patternvalid-palindromeโ left-to-right string scanning with character-level decisionsreverse-integerโ integer manipulation where overflow and digit extraction are the key challenges