Decode Ways Solution
Given a string of digits, return the number of ways to decode it, where 'A'=1, 'B'=2, โฆ, 'Z'=26.
Problem Statement
A message encoded as digits can be decoded to letters ('1'โ'A', โฆ, '26'โ'Z'). Given string s containing only digits, return the number of ways to decode it. Leading zeros are invalid ('06' is not valid, but '6' is).
Recursion โ O(2โฟ)
At each position, try decoding one digit (if valid) or two digits (if the pair is 10โ26). Recurse on the remaining suffix. The recursion branches at most twice per call.
Every valid decoding is a sequence of 1-digit and 2-digit groups covering the entire string. The recursion exhaustively explores all ways to partition the string into such groups.
decode(i) may be called from both decode(i-1) and decode(i-2). Total calls: O(2โฟ). Bottom-up DP with two variables solves each subproblem once in O(n).
| Metric | Value |
|---|---|
| Time | O(2โฟ) |
| Space | O(n) call stack |
Bottom-Up DP โ O(n) time, O(1) space
dp[i] = number of decode ways for s[0..i-1]. Transition: dp[i] += dp[i-1] if s[i-1] != '0' (single digit valid), and dp[i] += dp[i-2] if s[i-2..i-1] forms 10โ26. Only two previous values needed โ O(1) space.
- Let
prev2 = 1(dp[0] = 1 โ empty string has one decoding),prev1 = s[0] != '0' ? 1 : 0. - For i from 2 to n: compute
cur = 0. Ifs[i-1] != '0':cur += prev1. If10 โค int(s[i-2..i-1]) โค 26:cur += prev2. - Shift:
prev2 = prev1; prev1 = cur. - Return
prev1.
- "Count ways to partition/segment a string with validity constraints." The signal is: at each position, you decide whether to take 1 character or 2 characters, and each choice has validity conditions.
- This is a constrained Fibonacci variant. Also used in LC 639 (Decode Ways II โ with wildcards).
- '0' cannot be decoded alone (there's no letter for 0). It can only appear as the second digit of 10 or 20.
- If
s[i]=='0', the single-digit path contributes 0 ways. - If neither 10 nor 20 precedes it, there are 0 total ways โ the answer is 0 (impossible to decode).
Visual Walkthrough
Trace for s = "226".
Code โ Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Naive recursion | O(2โฟ) | O(n) | Exponential due to overlapping subproblems |
| Memoized recursion | O(n) | O(n) | Top-down with memo array |
| Two-variable DP โ optimal | O(n) | O(1) | Constant space; clean iterative |
>= 10 ensures the first digit of the pair is not zero.Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| Leading zero | "0" | prev1=0 immediately. Return 0. |
| Valid zero | "10" | "10"โJ. Only one way. dp: prev2=1, prev1=1, i=2: single '0' invalid, two-digit "10" valid โ cur=1. |
| Invalid zero | "30" | "30" > 26 โ invalid two-digit. Single '0' invalid. cur=0 โ return 0. |
| All ones | "111" | Like Fibonacci: 1,1,2,3. Answer=3. |
| Large string | 100 digits | O(100) iterations. Result may be large but fits in int for valid inputs. |
| Consecutive zeros | "100" | "10|0" โ trailing '0' with "00" not in 10โ26. Return 0. |
1 โค two โค 26 instead of 10 โค two โค 26. With the weaker bound, "06" would be treated as a valid two-digit code, but '06' has a leading zero and isn't a valid encoding. Always use 10 โค two โค 26 to exclude leading-zero pairs.