Example 1
ready- Input
s = "12"- Expected
2- Why
- decode as 1|2 or as 12
A string of digits can hide several messages. 12 might be two letters, or it might be
one — but a zero can't stand on its own.
Letters A through Z correspond to the strings "1" through "26". Given a
non-empty digit string s, return the number of different ways to split it into valid
letter codes.
A few ground rules:
"1" through "9"."10" through "26"."0" is invalid. Elsewhere, "0" works only as part of "10" or "20".0 when the whole string has no valid decoding.1 <= s.length <= 80s contains only the digits 0 through 9.2^31.Hints
From one position, you can consume one digit when it isn't 0. You can also consume
two digits when their value lies from 10 through 26.
Let ways(i) count valid decodings of the suffix starting at index i. Its answer
comes from the valid one-digit and two-digit choices, each followed by a smaller suffix.
When scanning left to right, the count for the current prefix reads only the previous prefix and the prefix two positions back. Roll those two values forward.
How would your transitions change if "*" could stand for any digit from 1 through 9?
Visible cases
s = "12"2s = "226"3s = "06"0Interview signal
Memoization saves a subproblem's answer the first time you solve it, then reuses that
answer on later visits. It's a top-down form of dynamic programming (DP), where
smaller saved answers build the final result. Define ways(i) as the number of decodings
for the suffix beginning at index i.
If s[i] is 0, that suffix contributes no decoding. Otherwise, consume one digit. When
s[i:i + 2] is from 10 through 26, consume two as a second choice. The
transition — the rule that combines those smaller answers — is:
ways(i) = ways(i + 1) + ways(i + 2)
The second term exists only when the two-digit code is valid.
def num_decodings(s: str) -> int:
memo: dict[int, int] = {}
def ways(index: int) -> int:
if index == len(s):
return 1
if s[index] == "0":
return 0
if index in memo:
return memo[index]
total = ways(index + 1)
if index + 1 < len(s) and 10 <= int(s[index:index + 2]) <= 26:
total += ways(index + 2)
memo[index] = total
return total
return ways(0)Each index is solved once, with constant work around its recursive calls. Time O(n). Space O(n) for the memo and recursion stack.
Scan prefixes from left to right. Let previous count decodings through the previous
digit and two_back count decodings through the digit before that. For the current digit:
0, add previous for the one-digit transition.10 through 26, add two_back for the two-digit transition.def num_decodings(s: str) -> int:
two_back = 1
previous = 0 if s[0] == "0" else 1
for i in range(1, len(s)):
current = 0
if s[i] != "0":
current += previous
if 10 <= int(s[i - 1:i + 1]) <= 26:
current += two_back
two_back, previous = previous, current
return previousThis is the same DP with old prefix entries discarded as soon as nothing can read them. Time O(n). Space O(1).
Ready to run.
3 cases are queued.
Visible testcase
Case 1
Expected
2