1980. Find Unique Binary String
Problem Description
You are given an array of strings nums
containing n
unique binary strings, where each binary string has exactly length n
. Your task is to find and return a binary string of length n
that does not appear in the nums
array.
For example, if n = 3
and nums = ["101", "011", "000"]
, you need to find a 3-character binary string that is not in this list, such as "111" or "001" or "010", etc.
The problem guarantees that:
- All strings in
nums
are unique (no duplicates) - Each string consists only of '0' and '1' characters
- Each string has exactly
n
characters - You need to return a string of the same length
n
Since there are 2^n
possible binary strings of length n
and the array only contains n
strings, there will always be at least 2^n - n
valid answers. You can return any one of them.
The solution approach cleverly uses the fact that for binary strings of length n
, the count of '1's can range from 0
to n
(giving n+1
possible counts). Since we only have n
strings in the array, by the Pigeonhole Principle, there must be at least one count value that doesn't appear among the strings in nums
. The solution finds such a count and constructs a binary string with that many '1's.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- No: The problem involves an array of binary strings, not nodes and edges as in graph problems.
Need to solve for kth smallest/largest?
- No: We're not looking for the kth smallest or largest element. We need to find any binary string that doesn't exist in the given array.
Involves Linked Lists?
- No: The problem deals with an array of strings, not linked lists.
Does the problem have small constraints?
- Yes: The constraints are notably small - we have
n
unique binary strings each of lengthn
, wheren
is typically small (oftenn ≤ 16
in this problem). The total possible binary strings of lengthn
is2^n
, and we only haven
strings in our array, leaving many possibilities to explore.
Brute force / Backtracking?
- Yes: With small constraints and the need to find a valid binary string that doesn't exist in the given set, we can use brute force or backtracking to:
- Generate possible binary strings
- Check if each generated string exists in the input array
- Return the first string that doesn't exist in the array
Conclusion: The flowchart suggests using a brute force/backtracking approach for finding a unique binary string. While the given solution uses a clever counting optimization, the problem can indeed be solved with backtracking by systematically generating binary strings and checking if they exist in the input array until we find one that doesn't.
Intuition
Let's think about this problem step by step. We have n
binary strings, each of length n
, and we need to find one binary string of length n
that isn't in our collection.
First observation: How many possible binary strings of length n
exist? It's 2^n
. For example, if n = 3
, we have 2^3 = 8
possible strings: "000", "001", "010", "011", "100", "101", "110", "111".
But we only have n
strings in our array. So if n = 3
, we only have 3 strings out of 8 possible ones. This means there are at least 2^n - n
strings that don't appear in our array. Since 2^n
grows exponentially while n
grows linearly, we're guaranteed to have many missing strings.
Now, here's the key insight: Instead of checking all 2^n
possibilities, we can be clever. Let's think about what makes binary strings different from each other. One distinguishing feature is the number of '1's they contain.
For a binary string of length n
, how many '1's can it have? It can have 0, 1, 2, ..., up to n
ones. That's n + 1
different possibilities for the count of '1's.
Here's where the Pigeonhole Principle comes in: We have n
strings, but n + 1
possible counts of '1's. This means at least one count value must be missing! If we track which counts appear in our input strings, we can find a count that doesn't appear and construct a string with that many '1's.
For example, if nums = ["101", "011", "000"]
:
- "101" has 2 ones
- "011" has 2 ones
- "000" has 0 ones
The counts we see are {0, 2}. We're missing count 1 and count 3. We can create any string with 1 one (like "100" or "010" or "001") or with 3 ones (like "111"), and it's guaranteed not to be in our input.
The solution uses a bitmask to efficiently track which counts have appeared, then finds the first missing count and constructs a string with that many consecutive '1's followed by '0's.
Learn more about Backtracking patterns.
Solution Approach
Building on our intuition about counting '1's, let's implement the solution step by step:
Step 1: Track which counts appear
We use a bitmask (an integer mask
) to efficiently track which counts of '1's appear in our input strings. The i
-th bit of mask
being 1 means we've seen a string with exactly i
ones.
mask = 0 for x in nums: mask |= 1 << x.count("1")
For each string x
in nums
:
- Count how many '1's it has using
x.count("1")
- Set the corresponding bit in
mask
using the bitwise OR operation|=
and left shift1 << count
For example, if a string has 2 ones, we set the 2nd bit of mask: mask |= 1 << 2
sets mask
's bit at position 2 to 1.
Step 2: Find a missing count
Now we need to find which count doesn't appear. We check each possible count from 0 to n
:
n = len(nums)
for i in range(n + 1):
if mask >> i & 1 ^ 1:
return "1" * i + "0" * (n - i)
Breaking down the condition mask >> i & 1 ^ 1
:
mask >> i
shiftsmask
right byi
positions& 1
extracts the least significant bit (checks if thei
-th bit was set)^ 1
XORs with 1, which flips the bit (0 becomes 1, 1 becomes 0)
This condition is true when the i
-th bit of mask
is 0, meaning no string in nums
has exactly i
ones.
Step 3: Construct the answer
Once we find a missing count i
, we construct a simple binary string with exactly i
ones:
"1" * i
createsi
consecutive ones"0" * (n - i)
fills the rest with zeros- Concatenating them gives us a string of length
n
with exactlyi
ones
This string is guaranteed not to exist in nums
because no string in nums
has this count of '1's.
Time Complexity: O(n²) - we iterate through n strings and count '1's in each (O(n) per string)
Space Complexity: O(1) - we only use a single integer mask
for tracking
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with n = 3
and nums = ["110", "011", "000"]
.
Step 1: Track which counts of '1's appear
We initialize mask = 0
(in binary: 0000
).
For each string, we count the '1's and set the corresponding bit in mask
:
- "110" has 2 ones →
mask |= 1 << 2
→mask = 0100
(bit 2 is set) - "011" has 2 ones →
mask |= 1 << 2
→mask = 0100
(bit 2 already set, no change) - "000" has 0 ones →
mask |= 1 << 0
→mask = 0101
(bit 0 is now also set)
Final mask = 0101
in binary, which means:
- Bit 0 is set: we have a string with 0 ones ✓
- Bit 1 is NOT set: we don't have a string with 1 one ✗
- Bit 2 is set: we have a string with 2 ones ✓
- Bit 3 is NOT set: we don't have a string with 3 ones ✗
Step 2: Find the first missing count
We check each possible count from 0 to 3:
i = 0
: Check if bit 0 is missing →(mask >> 0) & 1 ^ 1
=(0101 >> 0) & 1 ^ 1
=1 ^ 1
=0
(false, count 0 exists)i = 1
: Check if bit 1 is missing →(mask >> 1) & 1 ^ 1
=(0101 >> 1) & 1 ^ 1
=0 ^ 1
=1
(true, count 1 is missing!)
Step 3: Construct the answer
Since count 1 is missing, we create a string with exactly 1 one:
"1" * 1
= "1""0" * (3 - 1)
= "00"- Result: "100"
We return "100", which has exactly 1 one and is guaranteed not to be in our input array.
Verification: Our input was ["110", "011", "000"]. The string "100" is indeed not in this list, confirming our answer is correct.
Note that we could have continued checking and found that count 3 is also missing, which would give us "111" as another valid answer. The algorithm returns the first missing count it finds.
Solution Implementation
1class Solution:
2 def findDifferentBinaryString(self, nums: List[str]) -> str:
3 # Create a bitmask to track which counts of "1"s exist in the input
4 # If bit i is set, it means there's a string with i ones in nums
5 ones_count_mask = 0
6
7 # For each binary string, count its ones and mark that count in the mask
8 for binary_string in nums:
9 ones_count = binary_string.count("1")
10 ones_count_mask |= 1 << ones_count
11
12 # Get the length of the binary strings
13 string_length = len(nums)
14
15 # Find the first count of ones that doesn't exist in the input
16 # Check each possible count from 0 to n
17 for ones_count in range(string_length + 1):
18 # Check if this count of ones is NOT present in the mask
19 # (mask >> ones_count & 1) gets the bit at position ones_count
20 # XOR with 1 inverts it (0 becomes 1, 1 becomes 0)
21 if (ones_count_mask >> ones_count & 1) ^ 1:
22 # Create a string with 'ones_count' ones followed by zeros
23 return "1" * ones_count + "0" * (string_length - ones_count)
24
1class Solution {
2 public String findDifferentBinaryString(String[] nums) {
3 // Bitmask to track which counts of '1's exist in the input strings
4 // If bit i is set, it means there exists a string with i ones
5 int existingOnesCounts = 0;
6
7 // Count the number of '1's in each binary string
8 for (String binaryString : nums) {
9 int onesCount = 0;
10
11 // Count '1's in current binary string
12 for (int i = 0; i < binaryString.length(); i++) {
13 if (binaryString.charAt(i) == '1') {
14 onesCount++;
15 }
16 }
17
18 // Mark this count as existing by setting the corresponding bit
19 existingOnesCounts |= (1 << onesCount);
20 }
21
22 // Find the first count that doesn't exist
23 for (int targetOnesCount = 0; ; targetOnesCount++) {
24 // Check if this count is missing (bit is 0)
25 if ((existingOnesCounts >> targetOnesCount & 1) == 0) {
26 // Construct a binary string with targetOnesCount ones followed by zeros
27 // This guarantees a different string since no input has this exact count of ones
28 return "1".repeat(targetOnesCount) + "0".repeat(nums.length - targetOnesCount);
29 }
30 }
31 }
32}
33
1class Solution {
2public:
3 string findDifferentBinaryString(vector<string>& nums) {
4 // Bitmask to track which counts of '1's have been used
5 // If bit i is set, it means there exists a string with i ones
6 int usedOnesCounts = 0;
7
8 // For each binary string, count the number of '1's and mark that count as used
9 for (const auto& binaryStr : nums) {
10 int onesCount = count(binaryStr.begin(), binaryStr.end(), '1');
11 usedOnesCounts |= (1 << onesCount);
12 }
13
14 // Find the first unused count of '1's
15 for (int i = 0; ; ++i) {
16 // Check if bit i is not set (count i is unused)
17 if ((usedOnesCounts >> i & 1) ^ 1) {
18 // Construct a binary string with i ones followed by (n-i) zeros
19 // This guarantees a different string since no existing string has exactly i ones
20 return string(i, '1') + string(nums.size() - i, '0');
21 }
22 }
23 }
24};
25
1/**
2 * Finds a binary string of the same length as the input strings that is different from all of them.
3 * The algorithm works by tracking which counts of '1's are already used in the input strings,
4 * then finding the first unused count to construct a new unique binary string.
5 *
6 * @param nums - Array of binary strings of equal length
7 * @returns A binary string not present in the input array
8 */
9function findDifferentBinaryString(nums: string[]): string {
10 // Bitmask to track which counts of '1's are already used in the input strings
11 let usedOnesCounts: number = 0;
12
13 // For each binary string, count the number of '1's and mark that count as used
14 for (const binaryString of nums) {
15 // Count the number of '1' characters in the current binary string
16 const onesCount: number = binaryString.split('').filter((char: string) => char === '1').length;
17
18 // Set the bit at position 'onesCount' to 1 in the bitmask
19 usedOnesCounts |= 1 << onesCount;
20 }
21
22 // Find the first count of '1's that hasn't been used
23 for (let i: number = 0; ; i++) {
24 // Check if the bit at position i is 0 (meaning this count is unused)
25 if (((usedOnesCounts >> i) & 1) === 0) {
26 // Construct a binary string with i ones followed by (n-i) zeros
27 const stringLength: number = nums.length;
28 return '1'.repeat(i) + '0'.repeat(stringLength - i);
29 }
30 }
31}
32
Time and Space Complexity
The time complexity is O(L)
, where L
is the total length of all strings in nums
. This is because:
- The main loop iterates through each string in
nums
(n iterations) - For each string, we call
x.count("1")
which takesO(len(x))
time to scan through the string - Since each string has length
n
, and there aren
strings, the total time for processing all strings isO(n * n) = O(n²)
- However, the total length
L = n * n = n²
, so the complexity can be expressed asO(L)
- The second loop runs at most
n + 1
times withO(1)
operations, contributingO(n)
which is dominated byO(L)
The space complexity is O(1)
because:
- We only use a constant amount of extra space: the variable
mask
(an integer),n
(an integer), andi
(loop variable) - The bit manipulation operations don't require additional space proportional to the input size
- The output string construction
"1" * i + "0" * (n - i)
is not counted as part of space complexity as it's the required output
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Pigeonhole Principle Application
Pitfall: Developers might think they need to check all 2^n
possible binary strings or use a set to store all strings in nums
and then generate random strings until finding one not in the set. This leads to inefficient solutions with potentially exponential time complexity.
Why it happens: The problem statement mentions there are 2^n
possible strings, which can mislead developers into thinking about the problem in terms of all possible combinations rather than focusing on the constraint that we only have n
strings.
Solution: Remember that with n
strings and n+1
possible counts of '1's (from 0 to n), at least one count must be missing. Focus on this counting approach rather than generating all possibilities.
2. Off-by-One Error in Range
Pitfall: Using range(n)
instead of range(n + 1)
when checking for missing counts:
# Wrong - misses the case where all n ones could be the missing count
for ones_count in range(string_length):
if (ones_count_mask >> ones_count & 1) ^ 1:
return "1" * ones_count + "0" * (string_length - ones_count)
Why it happens: It's easy to forget that a string of length n
can have anywhere from 0 to n
ones (inclusive), giving us n+1
possible values, not just n
.
Solution: Always use range(n + 1)
to check all possible counts from 0 to n inclusive.
3. Incorrect Bit Manipulation Order
Pitfall: Writing the condition check incorrectly due to operator precedence:
# Wrong - this doesn't properly check if bit is unset if mask >> ones_count & 1 == 0: # & has lower precedence than == return "1" * ones_count + "0" * (string_length - ones_count)
Why it happens: Python's operator precedence can be tricky. The comparison ==
has higher precedence than bitwise &
, so the above evaluates as mask >> ones_count & (1 == 0)
.
Solution: Use parentheses for clarity or use the XOR approach as shown in the solution:
# Correct approaches: if (mask >> ones_count & 1) == 0: # Explicit parentheses # OR if (mask >> ones_count & 1) ^ 1: # XOR with 1 to invert
4. Integer Overflow Concerns (Language-Specific)
Pitfall: In languages with fixed-size integers, using bit manipulation with large n
values could cause issues if n > 31
(for 32-bit integers) or n > 63
(for 64-bit integers).
Why it happens: The solution uses 1 << count
which could overflow if count is large enough.
Solution:
- In Python, this isn't an issue as integers have arbitrary precision
- In other languages, either use a BitSet/boolean array instead of bitmask, or add a check:
# Alternative approach using a set instead of bitmask
counts_seen = set()
for binary_string in nums:
counts_seen.add(binary_string.count("1"))
for ones_count in range(len(nums) + 1):
if ones_count not in counts_seen:
return "1" * ones_count + "0" * (len(nums) - ones_count)
Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?
Recommended Readings
Backtracking Template Prereq DFS with States problems dfs_with_states Combinatorial search problems Combinatorial search problems involve finding groupings and assignments of objects that satisfy certain conditions Finding all permutations combinations subsets and solving Sudoku are classic combinatorial problems The time complexity of combinatorial problems often grows rapidly with the size of
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!