Before attempting this problem, you should be comfortable with:
We can think of building a string as making a series of choices. At each step, we either append zero number of '0's or one number of '1's. The recursion naturally explores all possible combinations by branching at each decision point. A string is "good" when its length falls within the range [low, high], so we count it whenever we reach a valid length.
0).high, stop and return 0.low, count this as one valid string.zero characters and one where we add one characters.10^9 + 7.Where is equal to the given value.
The recursive solution has overlapping subproblems because we may reach the same string length through different paths. For example, adding zero then one might give the same length as adding one then zero. Memoization stores the result for each length we have already computed, avoiding redundant calculations.
10^9 + 7.class Solution:
def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int:
mod = 10**9 + 7
dp = {}
def dfs(length):
if length > high:
return 0
if length in dp:
return dp[length]
dp[length] = 1 if length >= low else 0
dp[length] += dfs(length + zero) + dfs(length + one)
return dp[length] % mod
return dfs(0)Where is equal to the given value.
Instead of starting from length 0 and recursing forward, we can build the solution iteratively. For each length i, the number of ways to reach it equals the ways to reach length i - zero (then add zeros) plus the ways to reach length i - one (then add ones). This is similar to the classic coin change problem where we count combinations.
dp[0] = 1 (one way to have an empty string).1 to high.i, set dp[i] = dp[i - zero] + dp[i - one] (checking bounds).dp[i] values for lengths in the range [low, high].10^9 + 7.class Solution:
def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int:
dp = { 0 : 1 }
mod = 10**9 + 7
for i in range(1, high + 1):
dp[i] = (dp.get(i - one, 0) + dp.get(i - zero, 0)) % mod
return sum(dp[i] for i in range(low, high + 1)) % modWhere is equal to the given value.
When counting the number of good strings, the result can grow very large. Forgetting to apply the modulo 10^9 + 7 during intermediate calculations leads to integer overflow.
# Wrong: Only applying mod at the end
dp[i] = dp[i - zero] + dp[i - one] # Can overflow before mod is applied
# Correct: Apply mod after each addition
dp[i] = (dp[i - zero] + dp[i - one]) % modThe problem asks for strings with length in the range [low, high] inclusive. A common mistake is using exclusive bounds or checking > high instead of >= high when counting valid strings.
# Wrong: Missing strings of length exactly equal to low
if length > low: # Should be length >= low
# Correct
if length >= low:
count += 1In the bottom-up DP approach, forgetting to set dp[0] = 1 (representing one way to build an empty string) causes all subsequent values to be zero.
# Wrong: Missing base case
dp = [0] * (high + 1)
for i in range(1, high + 1):
dp[i] = dp.get(i - zero, 0) + dp.get(i - one, 0) # All zeros!
# Correct: Initialize base case
dp = [0] * (high + 1)
dp[0] = 1 # One way to have an empty string