Intuition
Define dp[i] = number of ways to decode the prefix s[:i]. The very last decoded letter consumes either one digit ('1'..'9') — contributing dp[i-1] ways — or two digits ('10'..'26') — contributing dp[i-2]. Add the valid contributions. Like Fibonacci, only the last two states matter, so two scalars suffice.
Explanation
The recurrence
For each new position i:
- If
s[i]is between'1'and'9'(i.e. not'0'), it can stand alone as a letter'A'..'I'etc. → adddp[i-1]. - If
s[i-1..i+1](the two-character substring ending ati) is between'10'and'26', it can pair with the previous digit → adddp[i-2].
Otherwise that path contributes 0.
Steps
- Empty / leading zero: if
sis empty ors[0] == '0', return0. (You can't start with'0'— there's no letter for it.) - Initialise
prev2 = 1(decoding empty prefix has 1 way) andprev1 = 1(decoding length-1 prefix has 1 way, given it's not'0'). - For
ifrom1tolen(s) - 1:- Start
cur = 0. - If
s[i] != '0', addprev1tocur. - Compute
two = int(s[i-1:i+1]). If10 <= two <= 26, addprev2tocur. - Roll:
prev2, prev1 = prev1, cur.
- Start
- Return
prev1.
Walk-through
s = "226". Length 3.
| i | char | s[i] != '0' → add prev1 | two-digit | 10 ≤ two ≤ 26 → add prev2 | cur | (prev2, prev1) after |
|---|---|---|---|---|---|---|
| — | — | (init) | — | — | — | (1, 1) |
| 1 | '2' | '2' != '0' → cur += 1 (→1) | "22" | yes → cur += 1 (→2) | 2 | (1, 2) |
| 2 | '6' | cur += 2 (→2) | "26" | yes → cur += 1 (→3) | 3 | (2, 3) |
Return prev1 = 3. The three decodings are BZ (2-26), BBF (2-2-6), VF (22-6). ✓
Another example, s = "06":
s[0] == '0' → return 0. ✓
s = "12":
| i | char | cur computation | (prev2, prev1) after |
|---|---|---|---|
| — | — | (init) | (1, 1) |
| 1 | '2' | cur=1+1=2 (single + double) | (1, 2) |
Return 2: AB, L. ✓
Why we initialise prev2 = prev1 = 1
Think of dp[0] (ways to decode empty prefix) = 1 (the trivial empty decoding). And dp[1] (assuming valid first char) = 1. The recurrence then matches Fibonacci-like sums.
Edge cases
s = "0": return0.- Embedded
'0': e.g."10"→1way (J);"100"→0ways ('00'is invalid as a pair, and'0'alone is invalid). The recurrence handles this naturally — both contributions are 0. - Two-digit > 26: doesn't contribute via the pair branch.
Complexity
- Time:
O(n)— one pass. - Space:
O(1)— two scalars.
Implementation
class Solution {
public int numDecodings(String s) {
if (s.isEmpty() || s.charAt(0) == '0') return 0;
int prev2 = 1, prev1 = 1;
for (int i = 1; i < s.length(); i++) {
int cur = 0;
if (s.charAt(i) != '0') cur += prev1;
int two = Integer.parseInt(s.substring(i - 1, i + 1));
if (two >= 10 && two <= 26) cur += prev2;
prev2 = prev1;
prev1 = cur;
}
return prev1;
}
}