Problem overview
The method CountBinaryStrings receives two integers, n and k, and returns how many binary strings of length n have exactly k ones, all of them at even positions. Positions are counted from 0, so the even positions are 0, 2, 4 and so on. For n = 6 and k = 2 the answer is 3: 101000, 100010 and 001010. In each, the ones sit at two of the positions 0, 2 and 4. The notes say 2 * k <= n <= 30.
Approach: decide one position at a time
Instead of looking at how a string ends, this lesson builds the string from left to right and pushes counts forward. Let dp[i, j] be the number of ways to fill the first i positions so that exactly j ones were placed. From each state, the next position, with index i, offers up to two choices:
- Place a 0. It is allowed at any position, and the number of ones stays
j. So the ways indp[i, j]flow intodp[i + 1, j]. - Place a 1. It is allowed only when
iis even, and only if fewer thankones have been placed so far (j < k). The ways flow intodp[i + 1, j + 1].
The starting state is dp[0, 0] = 1: zero positions filled, zero ones, one way (the empty prefix). After all n positions are decided, dp[n, k] holds the answer.
Trace n = 4 and k = 1. Positions 0 and 2 may hold the single one, so the answer should be 2. Starting at dp[0, 0] = 1: position 0 gives dp[1, 0] = 1 (a 0) and dp[1, 1] = 1 (a 1). Position 1 is odd, so only zeros move forward: dp[2, 0] = 1 and dp[2, 1] = 1. Position 2 is even: dp[3, 0] = 1, dp[3, 1] = 2, one way from carrying dp[2, 1] forward with a 0 (the one is at position 0) and one from placing a 1 on dp[2, 0] (the one is at position 2). Position 3 is odd and carries the values over: dp[4, 1] = 2.
Step by step
- Declare the table as a rectangular
int[,]withn + 1rows andk + 1columns. Zero is the default value of every cell. - Set
dp[0, 0] = 1as the single starting state. - Loop over positions
ifrom 0 ton - 1. Each pass fills rowi + 1from rowi, so the last pass fills rown. - Loop over the counts
jfrom 0 tokinclusive. Every reachable state has to pass its ways forward. - Place a zero: add
dp[i, j]todp[i + 1, j]. No condition is needed. - Test for a one: the position must be even (
i % 2 == 0) andj < k. - Place a one: add
dp[i, j]todp[i + 1, j + 1]. - Return
dp[n, k].
The solution in C#
class Solution {
public int CountBinaryStrings(int n, int k) {
int[,] dp = new int[n + 1, k + 1];
dp[0, 0] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= k; j++) {
dp[i + 1, j] += dp[i, j];
if (i % 2 == 0 && j < k) {
dp[i + 1, j + 1] += dp[i, j];
}
}
}
return dp[n, k];
}
}
C# details worth knowing:
- The
+=operator. Two different transitions can reach the same cell:dp[i + 1, j]receives fromdp[i, j](a 0) and fromdp[i, j - 1](a 1). Using+=accumulates both, whereas=would overwrite the first contribution. - Push versus pull. A table can be filled by computing each cell from earlier cells (pulling), or by having each cell send its value to later cells (pushing). This solution pushes. Both styles give the same table; pushing is natural when the rule is about what can be added next.
- The guard protects the array. When
j == k,dp[i + 1, j + 1]would be columnk + 1, which doesn't exist and would throwIndexOutOfRangeException. The conditionj < kprevents that, and&&evaluates it only after the parity test. - Remainder operator.
i % 2 == 0is true for 0, 2, 4. In C# the remainder keeps the sign of the dividend, which doesn't matter here becauseiis never negative.
Why the answer is a binomial coefficient
There is a shortcut worth knowing. The odd positions must hold zeros, and the even positions are free: there are (n + 1) / 2 of them (integer division), and a valid string picks exactly k of them for its ones. So the answer is "choose k from (n + 1) / 2". For n = 6 that is choosing 2 from 3 positions, which is 3. The table computes this count without needing a formula, and it generalises to rules that no formula covers.
Complexity
Time: O(n * k), a constant amount of work per cell. Space: O(n * k) for the table, reducible to two rows because each row depends only on the previous one.
Edge cases and common mistakes
k = 0. Only the all-zero string qualifies, and the table gives 1.- Counting from 1. The statement's example has ones at the first and third positions of "101000". That is positions 0 and 2 in zero-based terms, which is what the code uses. Treating the first character as position 1 would shift which positions are "even".
- Dropping the zero transition. Without
dp[i + 1, j] += dp[i, j], no string could ever contain a 0, so almost every count would be lost. - Using
=instead of+=. The one-transition into a cell would erase the zero-transition's contribution. - Dropping
j < k. The program would crash on the last column.
Testing
n = 6,k = 2gives 3.n = 4,k = 1gives 2.n = 4,k = 0gives 1.n = 6,k = 3gives 1: ones at positions 0, 2 and 4.