C++ 2466.cpp
class Solution {
public:
int countGoodStrings(int low, int high, int zero, int one) {
vector<int> dp(high + 1, 0);
dp[0] = 1;
const int MOD = 1000000007;
for (int i = 0; i < high; i++)
{
if (dp[i] == 0)
continue;
if (i + zero <= high)
{
dp[i + zero] += dp[i];
dp[i + zero] %= MOD;
}
if (i + one <= high)
{
dp[i + one] += dp[i];
dp[i + one] %= MOD;
}
}
int64_t sum = 0;
for (int i = low; i <= high; i++)
sum += dp[i];
return sum % MOD;
}
};
Difficulty
and has an acceptance rate of about 59% (300k+ submissions).
Typical patterns to solve this problem:
- Dynamic Programming
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation solves the combinatorial problem of counting binary strings whose lengths lie in the interval [low, high] and that can be constructed by repeatedly appending blocks of length zero (containing only ‘0’) or one (containing only ‘1’). The result is returned modulo 10⁹+7. The algorithm uses a one-dimensional dynamic programming (DP) array that records the number of ways to reach each possible total length up to high. After the DP table is filled, a final accumulation step sums the entries that fall within the required length range.
Key Steps
DP Table Initialization
The DP vector is sized to high + 1 and all entries are zeroed. The base case dp[0] = 1 represents the empty string, which serves as the starting point for building longer strings.
vector<int> dp(high + 1, 0);
dp[0] = 1;
const int MOD = 1000000007;
Iterative Transition
A single forward loop iterates over every length i from 0 to high-1. If dp[i] is zero the iteration is skipped, which avoids unnecessary work for unreachable lengths. For each reachable length, the algorithm attempts to extend the current string by a block of size zero and by a block of size one, provided the new length does not exceed high. The transition adds the number of ways to reach i to the target positions and applies the modulus to keep values bounded.
for (int i = 0; i < high; i++)
{
if (dp[i] == 0)
continue;
if (i + zero <= high)
{
dp[i + zero] += dp[i];
dp[i + zero] %= MOD;
}
if (i + one <= high)
{
dp[i + one] += dp[i];
dp[i + one] %= MOD;
}
}
Summation of Valid Lengths
After the DP table is complete, a second loop aggregates the counts for all lengths that satisfy the lower bound low. The accumulator is a 64-bit integer to prevent overflow before the final modulus operation.
int64_t sum = 0;
for (int i = low; i <= high; i++)
sum += dp[i];
return sum % MOD;
Edge Cases Handled
The algorithm gracefully handles the situation where low is zero. Because dp[0] is initialized to one, the empty string is counted if the interval includes zero. When zero or one equals zero, the transition checks i + zero <= high and i + one <= high prevent infinite loops because the loop index i only advances forward; a zero-length block would never increase i, but the condition i + zero <= high would still be true, however the continue guard on dp[i] == 0 ensures that only reachable states propagate. If both block sizes are larger than high, the inner updates are never executed, leaving dp[0] as the sole non-zero entry, and the final sum correctly reflects that only the empty string (if within range) is valid.
When high is very large, the DP vector allocation respects the exact bound, and the modulus operation after each addition guarantees that intermediate values never exceed the 32-bit integer range, preventing overflow in the DP array itself.
Performance Optimizations
The implementation incorporates several practical optimizations:
-
Sparse propagation guard - The
if (dp[i] == 0) continue;statement skips work for lengths that cannot be formed, reducing the number of arithmetic operations especially when the reachable lengths are sparse. -
In-place modulus - By applying
% MODimmediately after each addition, the algorithm avoids the need for a separate normalization pass and keeps the DP values within the range of a 32-bit signed integer, which improves cache friendliness. -
Single pass DP - The forward iteration from
0tohigh-1ensures that each state is processed exactly once, eliminating redundant recomputation that would arise in a recursive or memoized approach. -
Compact accumulator - Using a 64-bit accumulator only for the final summation isolates the larger integer arithmetic to a single loop, while the DP array itself remains 32-bit, reducing memory bandwidth.
These choices collectively yield a solution that runs in linear time with respect to high and uses linear additional space, while keeping the constant factors low through early exits and immediate modular reduction.
ELI5 - Simplified Explanation
Imagine building a long tower using two different types of blocks. One type of block is a specific length called zero, and the other type is a different length called one. You want to see how many different ways you can build towers that are at least as tall as low but no taller than high.
The code works like a giant list of boxes where each box tells you how many ways there are to reach a certain height. You start at the very bottom, where height zero has one way to exist, because having nothing already counts as a way to start.
Then, the computer goes through every single height one at a time. It checks how many ways it already found to reach the current size. Once it knows that, it tries adding the zero block and the one block to see what new heights it can reach next. It keeps track of these counts as it goes, always making sure the numbers stay manageable by using a special math trick.
If it ever discovers a way to reach a height that fits perfectly between low and high, it tucks that number away. After checking all the possible heights up to the maximum limit, the computer adds up every way it found that fits your special range to tell you the final count.
Code Quality
Complexity Analysis
As a rule of thumb, the maximum input size for one second execution time is
- factorial: up to 10 elements
- exponential: up to 20 elements
- polynomial: a few thousand elements
- loglinear: up to 100,000 elements
- linear: a few million elements
- logarithmic: a billion elements
- constant: practically unlimited
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||
|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||
| www | AlgoMonster | Java | Python | C++ | TS | |||
| WalkCCC | Java | Python | C++ | |||||
| leetcode.ca | Java | Python | JS | C++ | Go | TS | ||
| Progiez | Java | Python | C++ | |||||
| github | charles-wangkai | Java | ||||||
| fsq | C++ | |||||||
| JoshCrozier | JS | |||||||
| lkwq007 | Python | |||||||
| Shamik07 | Dart | |||||||
| varunu28 | Java | |||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||
| WalkCCC | C++, Python, Java | |||||||
| leetcode.ca | C++, Go, Java, JavaScript, Python, TypeScript | |||||||
| Progiez | C++, Java, Python | |||||||
| charles-wangkai | Java | |||||||
| fsq | C++ | |||||||
| JoshCrozier | JavaScript | |||||||
| lkwq007 | Python | |||||||
| Shamik07 | Dart | |||||||
| varunu28 | Java |
Videos
Similar Problems
- problem 70: Climbing Stairs
Solved On The Same Day
- problem 1: Two Sum
- problem 9: Palindrome Number
- problem 14: Longest Common Prefix
- problem 20: Valid Parentheses
- problem 26: Remove Duplicates from Sorted Array
- problem 27: Remove Element
- problem 83: Remove Duplicates from Sorted List
- problem 88: Merge Sorted Array
- problem 94: Binary Tree Inorder Traversal
- problem 100: Same Tree
- problem 104: Maximum Depth of Binary Tree
- problem 121: Best Time to Buy and Sell Stock
- problem 136: Single Number
- problem 141: Linked List Cycle
- problem 169: Majority Element
- problem 190: Reverse Bits
- problem 226: Invert Binary Tree
- problem 234: Palindrome Linked List
- problem 1114: Print in Order
- problem 3: Longest Substring Without Repeating Characters
- problem 7: Reverse Integer
- problem 8: String to Integer (atoi)
- problem 11: Container With Most Water
- problem 12: Integer to Roman
- problem 15: 3Sum
- problem 17: Letter Combinations of a Phone Number
- problem 22: Generate Parentheses
- problem 31: Next Permutation
- problem 36: Valid Sudoku
- problem 39: Combination Sum
- problem 40: Combination Sum II
- problem 45: Jump Game II
- problem 46: Permutations
- problem 47: Permutations II
- problem 48: Rotate Image
- problem 49: Group Anagrams
- problem 55: Jump Game
- problem 59: Spiral Matrix II
- problem 75: Sort Colors
- problem 77: Combinations
- problem 91: Decode Ways
- problem 95: Unique Binary Search Trees II
- problem 96: Unique Binary Search Trees
- problem 134: Gas Station
- problem 164: Maximum Gap
- problem 189: Rotate Array
- problem 215: Kth Largest Element in an Array
- problem 238: Product of Array Except Self
- problem 287: Find the Duplicate Number
- problem 322: Coin Change
- problem 347: Top K Frequent Elements
- problem 377: Combination Sum IV
- problem 438: Find All Anagrams in a String
- problem 474: Ones and Zeroes
- problem 518: Coin Change II
- problem 526: Beautiful Arrangement
- problem 763: Partition Labels
- problem 983: Minimum Cost For Tickets
- problem 994: Rotting Oranges
- problem 1027: Longest Arithmetic Subsequence
- problem 2140: Solving Questions With Brainpower
- problem 4: Median of Two Sorted Arrays
- problem 41: First Missing Positive
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality