|
| 1 | +# Single Pass Left-to-Right | 18 Lines | O(n) | 2ms |
| 2 | + |
| 3 | +# Intuition |
| 4 | +Roman numerals follow a pattern where symbols are generally read left-to-right with their values added together. The key insight is handling the subtraction cases (like IV = 4, IX = 9) where a smaller symbol precedes a larger one. We can detect these cases by looking ahead to the next symbol and applying subtraction when the current symbol is smaller than the next one. |
| 5 | + |
| 6 | +# Approach |
| 7 | +I'll use a single-pass algorithm with lookahead for subtraction detection: |
| 8 | + |
| 9 | +1. **Symbol Value Mapping**: Create a lookup table for each Roman symbol and its corresponding integer value. |
| 10 | + |
| 11 | +2. **Left-to-Right Processing**: Iterate through each character in the string from left to right. |
| 12 | + |
| 13 | +3. **Subtraction Rule Detection**: For each symbol, check if the next symbol has a higher value: |
| 14 | + - If yes: This is a subtraction case (like IV, IX, XL, etc.), so subtract the current value |
| 15 | + - If no: This is a normal additive case, so add the current value |
| 16 | + |
| 17 | +4. **Lookahead Safety**: When checking the next symbol, ensure we don't go out of bounds by checking if position + 1 exists. |
| 18 | + |
| 19 | +5. **Accumulate Result**: Keep a running total that increases or decreases based on whether we're in an additive or subtractive case. |
| 20 | + |
| 21 | +This approach naturally handles all Roman numeral patterns in a single pass without needing to preprocess or identify specific subtraction pairs. |
| 22 | + |
| 23 | +# Complexity |
| 24 | +- Time complexity: $$O(n)$$ |
| 25 | + - Single pass through the string examining each character once |
| 26 | + - Constant-time lookups in the symbol value map |
| 27 | + - Each character processed in constant time |
| 28 | + |
| 29 | +- Space complexity: $$O(1)$$ |
| 30 | + - Fixed-size lookup table with 7 Roman symbols |
| 31 | + - Only using constant extra variables for tracking position and total |
| 32 | + - Space usage doesn't scale with input size |
| 33 | + |
| 34 | +# Code |
| 35 | +```typescript [] |
| 36 | +const romanToInt = (s: string): number => { |
| 37 | + const romanSymbolValues: Record<string, number> = { |
| 38 | + I: 1, |
| 39 | + V: 5, |
| 40 | + X: 10, |
| 41 | + L: 50, |
| 42 | + C: 100, |
| 43 | + D: 500, |
| 44 | + M: 1000 |
| 45 | + }; |
| 46 | + |
| 47 | + let totalValue = 0; |
| 48 | + |
| 49 | + for (let position = 0; position < s.length; position++) { |
| 50 | + const currentSymbolValue = romanSymbolValues[s[position]]; |
| 51 | + const nextSymbolValue = romanSymbolValues[s[position + 1]]; |
| 52 | + |
| 53 | + if (nextSymbolValue && currentSymbolValue < nextSymbolValue) { |
| 54 | + totalValue -= currentSymbolValue; |
| 55 | + } else { |
| 56 | + totalValue += currentSymbolValue; |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + return totalValue; |
| 61 | +}; |
| 62 | +``` |
0 commit comments