A message containing letters from A-Z
can be encoded into numbers using the following mapping:
'A' -> "1" 'B' -> "2" ... 'Z' -> "26"
To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example, "11106"
can be mapped into:
"AAJF"
with the grouping (1 1 10 6)
"KJF"
with the grouping (11 10 6)
Note that the grouping (1 11 06)
is invalid because "06"
cannot be mapped into 'F'
since "6"
is different from "06"
.
In addition to the mapping above, an encoded message may contain the '*'
character, which can represent any digit from '1'
to '9'
('0'
is excluded). For example, the encoded message "1*"
may represent any of the encoded messages "11"
, "12"
, "13"
, "14"
, "15"
, "16"
, "17"
, "18"
, or "19"
. Decoding "1*"
is equivalent to decoding any of the encoded messages it can represent.
Given a string s
consisting of digits and '*'
characters, return the number of ways to decode it.
Since the answer may be very large, return it modulo 109 + 7
.
Example 1:
Input: s = "*" Output: 9 Explanation: The encoded message can represent any of the encoded messages "1", "2", "3", "4", "5", "6", "7", "8", or "9". Each of these can be decoded to the strings "A", "B", "C", "D", "E", "F", "G", "H", and "I" respectively. Hence, there are a total of 9 ways to decode "*".
Example 2:
Input: s = "1*" Output: 18 Explanation: The encoded message can represent any of the encoded messages "11", "12", "13", "14", "15", "16", "17", "18", or "19". Each of these encoded messages have 2 ways to be decoded (e.g. "11" can be decoded to "AA" or "K"). Hence, there are a total of 9 * 2 = 18 ways to decode "1*".
Example 3:
Input: s = "2*" Output: 15 Explanation: The encoded message can represent any of the encoded messages "21", "22", "23", "24", "25", "26", "27", "28", or "29". "21", "22", "23", "24", "25", and "26" have 2 ways of being decoded, but "27", "28", and "29" only have 1 way. Hence, there are a total of (6 * 2) + (3 * 1) = 12 + 3 = 15 ways to decode "2*".
Constraints:
1 <= s.length <= 105
s[i]
is a digit or '*'
.This problem asks to find the number of ways to decode a string consisting of digits and '' characters, where '' can represent any digit from '1' to '9'. The solution uses dynamic programming to efficiently solve this.
Approach:
The core idea is to build a dynamic programming solution where dp[i]
represents the number of ways to decode the substring s[0...i-1]
. We iterate through the string, considering both one-digit and two-digit decoding possibilities at each step. The presence of '' adds complexity, as we need to consider multiple possibilities for each ''.
State Transition:
For each position i
, we calculate dp[i]
based on the previous states:
One-digit decoding: If s[i-1]
is a digit other than '0', it contributes dp[i-1]
ways (because we can append this digit independently). If s[i-1]
is '', it contributes 9 * dp[i-1]
ways (since '' can be any digit from 1 to 9).
Two-digit decoding: If s[i-2]
and s[i-1]
form a valid two-digit code (between 10 and 26, inclusive), this adds dp[i-2]
ways. We need to handle the cases where s[i-2]
or s[i-1]
or both are ''. Each of these "" cases requires careful consideration of the possible combinations.
Base Cases:
dp[0] = 1
(empty substring has one decoding)dp[1]
depends on whether s[0]
is a digit, '0', or '*'.Modulo Operation:
To handle potentially large numbers of decoding possibilities, the modulo operator (% 1000000007
) is used throughout the calculation.
Time Complexity:
The solution iterates through the input string once. The calculation for each position takes constant time, even with the handling of '*' characters, which is a set of fixed conditions. Therefore, the overall time complexity is O(n), where n is the length of the string.
Space Complexity:
The solution utilizes constant extra space to store the three variables, a
, b
, and c
that represent the dp states. Hence, the space complexity is O(1).
class Solution:
def numDecodings(self, s: str) -> int:
mod = int(1e9 + 7)
n = len(s)
# dp[i - 2], dp[i - 1], dp[i]
a, b, c = 0, 1, 0 # Initialize dp states
for i in range(1, n + 1):
# 1 digit
if s[i - 1] == "*":
c = 9 * b % mod # * can be any digit 1-9
elif s[i - 1] != "0":
c = b # valid single digit
else:
c = 0 # 0 cannot be a single digit
# 2 digits
if i > 1:
# Handle all combinations of '*' in two digit codes
if s[i - 2] == "*" and s[i - 1] == "*":
c = (c + 15 * a) % mod
elif s[i - 2] == "*":
if s[i - 1] > "6":
c = (c + a) % mod
else:
c = (c + 2 * a) % mod
elif s[i - 1] == "*":
if s[i - 2] == "1":
c = (c + 9 * a) % mod
elif s[i - 2] == "2":
c = (c + 6 * a) % mod
elif (
s[i - 2] != "0"
and (ord(s[i - 2]) - ord("0")) * 10 + ord(s[i - 1]) - ord("0") <= 26
):
c = (c + a) % mod
a, b = b, c # Update dp states
return c
The Java and Go code follow the same logic with minor syntax changes to accommodate the respective language features. The core dynamic programming strategy and the handling of '*' remain consistent.