Problem overview
The method countBinaryStrings(_ n: Int, _ k: Int) counts binary strings of length n that contain exactly k ones, where every one sits on an even position. Positions start at 0, so the even positions are 0, 2, 4 and so on. Odd positions must hold a 0.
For n = 6 and k = 2 the answer is 3: 101000, 100010 and 001010. The ones are always at positions among 0, 2 and 4. The notes guarantee 2 * K <= N <= 30.
Approach: decide one position at a time
The table dp[i][j] counts the strings of length i that contain j ones and obey the rule. This lesson fills the table "forward": each cell hands its count to the cells it can grow into when the next character is chosen.
At position i there are two choices for the next character:
- A
0is always allowed. It keeps the number of ones the same, sodp[i][j]contributes todp[i + 1][j]. - A
1is allowed only ifiis even and fewer thankones have been used. It raises the count by one, sodp[i][j]contributes todp[i + 1][j + 1].
Because the table starts at zero and cells only receive additions, the order is simple: finish row i completely before using it to fill row i + 1. The loop over i guarantees that.
A quick check of the idea: only the even positions can hold ones, and there are (n + 1) / 2 of them, rounded down by integer division. The answer is the number of ways to choose k of them. For n = 6 that is choosing 2 of 3 positions, which is 3. The table computes the same number without needing that formula.
Step by step
- Create the table as a
varwithn + 1rows andk + 1columns of zeros. - Seed it with
dp[0][0] = 1: one empty string, zero ones. - Loop over positions
ifrom 0 ton - 1. Each pass decides the character at positioniand fills rowi + 1. The last pass fills rown. - Loop over the one-counts
jfrom 0 tok, so every reachable state passes its count forward. - Place a 0. Add
dp[i][j]todp[i + 1][j], with no condition. - Guard the 1. The branch runs only when
iis even andj < k. - Place a 1. Add
dp[i][j]todp[i + 1][j + 1]. - Return
dp[n][k]: full length, exactlykones.
The solution in Swift
class Solution {
func countBinaryStrings(_ n: Int, _ k: Int) -> Int {
var dp = [[Int]](repeating: [Int](repeating: 0, count: k + 1), count: n + 1)
dp[0][0] = 1
for i in 0..<n {
for j in 0...k {
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]
}
}
Swift details worth knowing:
+=on a nested subscript.dp[i + 1][j] += dp[i][j]reads and writes the inner array element in place. It works becausedpis avar.- Two range operators.
0..<nstops beforen, which is right for positions, because the last position isn - 1.0...kincludesk, which is right for counts, because the table has columns 0 throughk. Forn = 0, the range0..<0is empty and the loop is skipped. A range with a lower bound above its upper bound would stop the program, but neither range here can have one whennandkare non-negative. - Short-circuit
&&.j < kis checked only wheni % 2 == 0holds, and either way it must be true before the code touches columnj + 1. This keeps the index inside thek + 1columns. Swift arrays check bounds and crash on an invalid index instead of reading other memory. - Remainder.
i % 2 == 0is true for even non-negativei, which is all this loop produces.
Watching the table fill
Take n = 4 and k = 1. Row 0 is [1, 0].
- Position 0 (even). From
dp[0][0] = 1, a 0 givesdp[1][0] = 1and a 1 givesdp[1][1] = 1. Row 1 is[1, 1]. - Position 1 (odd). Only zeros can be placed. Row 2 is a copy of row 1:
[1, 1]. - Position 2 (even). The zero transition copies row 2 into row 3, giving
[1, 1]. The one transition addsdp[2][0] = 1todp[3][1], so row 3 is[1, 2]. The celldp[2][1]is not extended becausej = k. - Position 3 (odd). Row 4 is a copy of row 3:
[1, 2].
The answer is dp[4][1] = 2: the strings 1000 and 0010. The one-count cap shows up too: a cell with j = k can still receive zeros, but it never passes a 1 on, so no string ever ends up with more than k ones.
Complexity
Time: O(n * k). There are n positions and k + 1 counts, and each cell does a constant amount of work.
Space: O(n * k). The table has (n + 1) * (k + 1) cells. Each row depends only on the one before it, so two rows would be enough.
Edge cases and common mistakes
- Counting positions from 1. The statement's example shows ones at positions 0 and 2 when positions start at 0. Using
i % 2 == 1by mistake would allow ones only at positions 1, 3 and 5, and the answer forn = 5,k = 3would be 0 instead of 1. - Dropping the
j < kguard. Forj = kthe code would write to columnk + 1, which does not exist, and the program would crash. - Putting the zero transition inside the
if. Odd positions would then pass nothing forward, and almost every count would be 0. k = 0. The answer is 1, the all-zeros string: the 1-branch never runs.- Odd
n. Withn = 5the even positions are 0, 2 and 4, sok = 3gives 1.
Testing
n = 6,k = 2gives3.n = 5,k = 3gives1.n = 4,k = 0gives1.n = 1,k = 1gives1.n = 30,k = 15gives1.
