Problem overview
The method countBinaryStrings(n, k) counts the binary strings of length n that contain exactly k ones, where every one sits on an even position. Positions are counted from 0, so the even positions are 0, 2, 4 and so on. The notes guarantee 2 * k <= n <= 30.
For n = 6 and k = 2 the even positions are 0, 2 and 4. The valid strings are 101000 (ones at positions 0 and 2), 100010 (positions 0 and 4) and 001010 (positions 2 and 4), so the answer is 3. The odd positions always hold zeros.
Approach: decide one character at a time
A direct count is possible here: the odd positions are fixed, so the problem is really about choosing which k of the even positions get a one. The lesson solves it with the same table method as the plain version of this problem, which carries over to rules that are less regular.
Let dp(i)(j) be the number of valid prefixes of length i that contain j ones. The table is filled forward: each prefix is extended by the character at position i, and the number of ways is passed on to the longer prefix.
- A
0can go at any position and doesn't change the count of ones, sodp(i)(j)flows intodp(i + 1)(j). - A
1may go only at an eveni, and only if the prefix still has room for another one, sodp(i)(j)also flows intodp(i + 1)(j + 1)wheniis even andj < k.
Here is the table for n = 6 and k = 2. Each row lists the counts for 0, 1 and 2 ones:
length 0: 1 0 0
length 1: 1 1 0
length 2: 1 1 0
length 3: 1 2 1
length 4: 1 2 1
length 5: 1 3 3
length 6: 1 3 3
The rows for lengths 2, 4 and 6 repeat the row before them, because the position just added was odd and could only take a zero. The last row ends in 3, the answer.
Step by step
- Create the table with
Array.fill(n + 1, k + 1)(0). The two dimensions come first and the fill value in a second pair of parentheses. - Set
dp(0)(0)to 1. The empty prefix is the one starting point. - Loop over the positions with
0 until n. Iterationidecides the character at positioniand writes into rowi + 1, so the last pass (i = n - 1) writes rown. - Loop over the counts of ones with
0 to k. The upper bound is inclusive because the table hask + 1columns. - Place a zero: add
dp(i)(j)todp(i + 1)(j). No condition is needed. - Test whether a one is allowed:
i % 2 == 0 && j < k. - Place a one: add
dp(i)(j)todp(i + 1)(j + 1). - Return
dp(n)(k).
The solution in Scala
class Solution {
def countBinaryStrings(n: Int, k: Int): Int = {
val dp = Array.fill(n + 1, k + 1)(0)
dp(0)(0) = 1
for (i <- 0 until n) {
for (j <- 0 to 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)
}
}
Some Scala details in this code:
Array.fill(n + 1, k + 1)(0)takes the dimensions in the first parameter list and the value in the second. The value is evaluated for each cell, and the result is anArray[Array[Int]]with separate rows.+=on an array element.dp(i + 1)(j) += dp(i)(j)is shorthand fordp(i + 1)(j) = dp(i + 1)(j) + dp(i)(j). Scala rewrites it into a call toupdate, so it works on array cells even thoughdpis aval.0 until nagainst0 to k. The first excludes its end and the second includes it. The positions run up ton - 1because the code writes toi + 1, while the counts run up tokbecausekis a valid column.&&short-circuits. Wheniis odd, the second half of the condition isn't evaluated.%with non-negative numbers behaves as expected.iis never negative here, soi % 2 == 0is a safe parity test.
Complexity
Time: O(n * k). The two loops visit about n * (k + 1) cells, each with a constant amount of work.
Space: O(n * k). The table has (n + 1) * (k + 1) cells.
Edge cases and common mistakes
- Dropping the
j < kcheck. Whenjequalsk,dp(i + 1)(j + 1)is past the last column and the method throws anArrayIndexOutOfBoundsException. The check also stops strings with more thankones from being counted. - Using 1-based positions. If position 1 were treated as the first position, the even test would pick the wrong characters. With
istarting at 0, the testi % 2 == 0matches the example: ones at positions 0, 2 and 4. k = 0. Only the all-zeros string qualifies.countBinaryStrings(4, 0)returns 1.- The boundary of the notes. With
n = 5andk = 3the even positions 0, 2 and 4 must all hold ones, and the method returns 1. Forn = 4andk = 3, which the notes rule out, there are only two even positions and the method returns 0. - Largest input. For
n = 30andk = 15all fifteen even positions are used, and the answer is 1. Counts never get near theIntlimit here, because there are at most 15 positions to choose from. - Skipping the zero update. If only the "place a one" branch ran, the table could not carry a prefix across an odd position, and the counts would drop to 0.
Testing
n = 6,k = 2gives3.n = 4,k = 1gives2.n = 4,k = 0gives1.n = 5,k = 3gives1.n = 30,k = 15gives1.
