Facebook Pixel

1980. Find Unique Binary String

MediumArrayHash TableStringBacktracking
Leetcode Link

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 length n, where n is typically small (often n ≤ 16 in this problem). The total possible binary strings of length n is 2^n, and we only have n 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:
    1. Generate possible binary strings
    2. Check if each generated string exists in the input array
    3. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 shift 1 << 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 shifts mask right by i positions
  • & 1 extracts the least significant bit (checks if the i-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 creates i consecutive ones
  • "0" * (n - i) fills the rest with zeros
  • Concatenating them gives us a string of length n with exactly i 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 Evaluator

Example 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 << 2mask = 0100 (bit 2 is set)
  • "011" has 2 ones → mask |= 1 << 2mask = 0100 (bit 2 already set, no change)
  • "000" has 0 ones → mask |= 1 << 0mask = 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 takes O(len(x)) time to scan through the string
  • Since each string has length n, and there are n strings, the total time for processing all strings is O(n * n) = O(n²)
  • However, the total length L = n * n = n², so the complexity can be expressed as O(L)
  • The second loop runs at most n + 1 times with O(1) operations, contributing O(n) which is dominated by O(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), and i (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)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More