Decode Ways
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".
Given a string s containing only digits, return the number of ways to decode it.
The test cases are generated so that the answer fits in a 32-bit integer.
Example 1:
Input: s = "12"
Output: 2
Explanation: "12" could be decoded as "AB" (1 2) or "L" (12).
Example 2:
Input: s = "226"
Output: 3
Explanation: "226" could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).
Example 3:
Input: s = "06"
Output: 0
Explanation: "06" cannot be mapped to "F" because of the leading zero ("6" is different from "06").
Intuition
For this question, the best way is to manually go through one example first.
- One example for happy scenario
- One example for unhappy/edge cases
We define a number is convertible if it's within the range of 1 -> 26
Happy scenario
Let's say as of happy example, we have 123. Let's try to walk through this number
- First, to parse
123, we need to parse the1in123.- The number of way to parse
1is1
- The number of way to parse
- Next, we want to parse
12in123.- In order to parse
12, it'sparse(1)2andparse(12)- The number of way to parse
1is1. Therefore we can make1way - But
12can also parse by itself. So we+1to our number of ways, so2ways
- The number of way to parse
- In order to parse
- Next, we want to parse
123in123.- In order to parse
123, it'sparse(12)3andparse(1)23parse(12)is the above so it's just2parse(1)is also the above so it's just1- So it's basically
3
- In order to parse
As we can see, for the happy scenario, we have the formula:
if isConvertible(i) and isConvertible(i - 1 : i + 1):
result[i] = result[i - 1] + result[i - 2]
Unhappy scenario
Let's consider the scenario 2101
- The number of way to
parse(2)is1 - We want to parse
21which can beparse(2)1andparse(21). So it's2 - We want to parse
210:- We cannot do
parse(21)0since0is invalid number. Therefore we can only doparse(2)10. Which isdp[i - 2] = 1
- We cannot do
- We want to parse
2101:- We can do
parse(210)1but notparse(21)01since01is not a valid number. Therefore we chooseparse(210)which isdp[i - 1] = 1
- We can do
So we have another formula:
if isConvertible(i) but not isConvertible(i - 1):
result[i] = result[i - 1]
if not isConvertible(i) but isConvertible(i-1):
result[i] = result[i - 2]
else:
# As of the question, if it's invalid return 0
return 0
Identify 1-D or 2-D array
As a result, this is similar to House Robbery of fibonnaci pattern. Therefore we can use 1-D array.
It's important to note that the calculation can start at the second index already. For example 10, we need to calculate at 0 already so we can shift everything to the right by 1
So for example if we have number 10 our dp array will be:
1 0
1,1,1
Where we initialise the first element is 1, the reason why it's 1 and not 0 is because in the case of happy scenario, for example
1,1
1,1,2
We can still follow the same formula and has 2
Implementation
class Solution:
def numDecodings(self, s: str):
if (s[0] == "0"): return 0
dp = [0 for _ in range(len(s) + 1)]
dp[0] = 1
dp[1] = 1
for i in range(2, len(s) + 1):
canConvertThisNumber = self.isConvertible(s[i - 1])
# Since everything is shifted to the right, we - 1 to get the value of S
canConvertThisAndPreviousNumberMerged = self.isConvertible(s[(i - 1) - 1: (i + 1) - 1])
if canConvertThisNumber and canConvertThisAndPreviousNumberMerged:
dp[i] = dp[i - 2] + dp[i - 1]
elif canConvertThisNumber and not canConvertThisAndPreviousNumberMerged:
dp[i] = dp[i - 1]
elif not canConvertThisNumber and canConvertThisAndPreviousNumberMerged:
dp[i] = dp[i - 2]
else:
return 0
return dp[len(s)]
def isConvertible(self, s):
if s[0] == "0": return False
num = int(s)
return num >= 1 and num <= 26
Time complexity: $O(n)$
Space complexity: $O(n)$ — could be optimise to $O(2)$ by using 2 variables only. See House Robbery > Optimisation