Before attempting this problem, you should be comfortable with:
Hash Sets - Using sets for O(1) lookup to check if a string exists
Backtracking/Recursion - Building solutions character by character and exploring possibilities
Binary String Manipulation - Converting between integers and binary representations
Cantor's Diagonal Argument (Optional) - A mathematical proof technique that guarantees constructing a unique element
1. Backtracking (Recursion)
Intuition
We need to find any binary string of length n that is not in the given array. Since there are 2^n possible strings but only n strings in the input, at least one must be missing. We can systematically try building strings character by character, checking at each complete string whether it exists in the set.
Algorithm
Store all input strings in a hash set for O(1) lookup.
Use backtracking starting with a string of all zeros.
At position i:
If i == n, check if the current string is in the set. If not, return it.
Try keeping position i as '0' and recurse.
If that fails, change position i to '1' and recurse.
classSolution:deffindDifferentBinaryString(self, nums: List[str])->str:
strSet ={s for s in nums}defbacktrack(i, cur):if i ==len(nums):
res ="".join(cur)returnNoneif res in strSet else res
res = backtrack(i +1, cur)if res:return res
cur[i]="1"return backtrack(i +1, cur)return backtrack(0,["0"for _ in nums])
funcfindDifferentBinaryString(nums []string)string{
strSet :=make(map[string]bool)for_, s :=range nums {
strSet[s]=true}
n :=len(nums)
cur :=make([]byte, n)for i :=range cur {
cur[i]='0'}var backtrack func(i int)string
backtrack =func(i int)string{if i == n {
res :=string(cur)if!strSet[res]{return res
}return""}
res :=backtrack(i +1)if res !=""{return res
}
cur[i]='1'returnbacktrack(i +1)}returnbacktrack(0)}
class Solution {funfindDifferentBinaryString(nums: Array<String>): String {val strSet = nums.toHashSet()val n = nums.size
funbacktrack(i: Int, cur: CharArray): String?{if(i == n){val res =String(cur)returnif(res in strSet)nullelse res
}
cur[i]='0'val res =backtrack(i +1, cur)if(res !=null)return res
cur[i]='1'returnbacktrack(i +1, cur)}returnbacktrack(0,CharArray(n){'0'})!!}}
classSolution{funcfindDifferentBinaryString(_ nums:[String])->String{let strSet =Set(nums)let n = nums.count
var cur =[Character](repeating:"0", count: n)funcbacktrack(_ i:Int)->String?{if i == n {let res =String(cur)return strSet.contains(res)?nil: res
}
cur[i]="0"iflet res =backtrack(i +1){return res
}
cur[i]="1"returnbacktrack(i +1)}returnbacktrack(0)!}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(n)
2. Backtracking (Iteration)
Intuition
Instead of recursive backtracking, we can iterate through all possible binary strings from 0 to n (we only need n+1 candidates since there are n input strings). Convert each number to its binary representation, pad it to length n, and check if it exists in the set.
Algorithm
Store all input strings in a hash set.
Iterate num from 0 to n:
Convert num to a binary string and pad with leading zeros to length n.
If this string is not in the set, return it.
Return empty string (though we are guaranteed to find one within n+1 attempts).
classSolution:deffindDifferentBinaryString(self, nums: List[str])->str:
strSet =set(nums)
n =len(nums)for num inrange(1<< n):
res =bin(num)[2:].zfill(n)if res notin strSet:return res
return""
publicclassSolution{publicStringfindDifferentBinaryString(String[] nums){Set<String> strSet =newHashSet<>();for(String s : nums){
strSet.add(s);}int n = nums.length;for(int num =0; num <(n +1); num++){String res =String.format("%"+ n +"s",Integer.toBinaryString(num)).replace(' ','0');if(!strSet.contains(res)){return res;}}return"";}}
classSolution{public:
string findDifferentBinaryString(vector<string>& nums){
unordered_set<string>strSet(nums.begin(), nums.end());int n = nums.size();for(int num =0; num <(n +1); num++){
string res =toBinaryString(num, n);if(strSet.find(res)== strSet.end()){return res;}}return"";}private:
string toBinaryString(int num,int length){
string res ="";for(int i = length -1; i >=0; i--){
res +=(num &(1<< i))?'1':'0';}return res;}};
classSolution{/**
* @param {string[]} nums
* @return {string}
*/findDifferentBinaryString(nums){const strSet =newSet(nums);const n = nums.length;for(let num =0; num < n +1; num++){let res = num.toString(2).padStart(n,'0');if(!strSet.has(res)){return res;}}return'';}}
publicclassSolution{publicstringFindDifferentBinaryString(string[] nums){HashSet<string> strSet =newHashSet<string>(nums);int n = nums.Length;for(int num =0; num < n +1; num++){string res = Convert.ToString(num,2).PadLeft(n,'0');if(!strSet.Contains(res)){return res;}}return"";}}
funcfindDifferentBinaryString(nums []string)string{
strSet :=make(map[string]bool)for_, s :=range nums {
strSet[s]=true}
n :=len(nums)for num :=0; num <= n; num++{
res := fmt.Sprintf("%0*b", n, num)if!strSet[res]{return res
}}return""}
class Solution {funfindDifferentBinaryString(nums: Array<String>): String {val strSet = nums.toHashSet()val n = nums.size
for(num in0..n){val res = Integer.toBinaryString(num).padStart(n,'0')if(res !in strSet){return res
}}return""}}
classSolution{funcfindDifferentBinaryString(_ nums:[String])->String{let strSet =Set(nums)let n = nums.count
for num in0...n {var res =String(num, radix:2)while res.count < n {
res ="0"+ res
}if!strSet.contains(res){return res
}}return""}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(n)
3. Cantor's Diagonal Argument
Intuition
Cantor's diagonal argument provides an elegant O(n) solution. For each string nums[i], we look at its i-th character and flip it. The resulting string differs from nums[0] at position 0, from nums[1] at position 1, and so on. This guarantees the constructed string differs from every input string at at least one position.
Algorithm
Create an empty result string.
For each index i from 0 to n-1:
Look at character nums[i][i] (the diagonal).
Append the opposite character: if it is '0', append '1'; if '1', append '0'.
classSolution:deffindDifferentBinaryString(self, nums: List[str])->str:
res =[]for i inrange(len(nums)):if nums[i][i]=='0':
res.append('1')else:
res.append('0')return"".join(res)
publicclassSolution{publicStringfindDifferentBinaryString(String[] nums){StringBuilder res =newStringBuilder();for(int i =0; i < nums.length; i++){
res.append(nums[i].charAt(i)=='0'?'1':'0');}return res.toString();}}
classSolution{public:
string findDifferentBinaryString(vector<string>& nums){
string res;for(int i =0; i < nums.size(); i++){
res +=(nums[i][i]=='0')?'1':'0';}return res;}};
classSolution{/**
* @param {string[]} nums
* @return {string}
*/findDifferentBinaryString(nums){let res =[];for(let i =0; i < nums.length; i++){
res.push(nums[i][i]==='0'?'1':'0');}return res.join('');}}
publicclassSolution{publicstringFindDifferentBinaryString(string[] nums){StringBuilder res =newStringBuilder();for(int i =0; i < nums.Length; i++){
res.Append(nums[i][i]=='0'?'1':'0');}return res.ToString();}}
funcfindDifferentBinaryString(nums []string)string{
res :=make([]byte,len(nums))for i :=0; i <len(nums); i++{if nums[i][i]=='0'{
res[i]='1'}else{
res[i]='0'}}returnstring(res)}
class Solution {funfindDifferentBinaryString(nums: Array<String>): String {val res =StringBuilder()for(i in nums.indices){
res.append(if(nums[i][i]=='0')'1'else'0')}return res.toString()}}
classSolution{funcfindDifferentBinaryString(_ nums:[String])->String{var res =""for i in0..<nums.count {let index = nums[i].index(nums[i].startIndex, offsetBy: i)
res += nums[i][index]=="0"?"1":"0"}return res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity:
O(1) extra space.
O(n) space for the output string.
4. Randomization
Intuition
Since there are 2^n possible strings but only n are in the input, randomly generating a string has a high probability of being unique. For small n, this probability is at least (2^n - n) / 2^n, which approaches 1 quickly. We keep generating random strings until we find one not in the set.
Algorithm
Store all input strings in a hash set.
Loop indefinitely:
Generate a random binary string of length n by randomly choosing '0' or '1' for each position.
If the string is not in the set, return it.
The expected number of attempts is very small due to the sparsity of input strings.
classSolution:deffindDifferentBinaryString(self, nums: List[str])->str:
strSet =set(nums)
n =len(nums)whileTrue:
res ="".join(random.choice("01")for _ inrange(n))if res notin strSet:return res
publicclassSolution{publicStringfindDifferentBinaryString(String[] nums){Set<String> strSet =newHashSet<>();for(String s : nums){
strSet.add(s);}int n = nums.length;Random random =newRandom();while(true){StringBuilder res =newStringBuilder();for(int i =0; i < n; i++){
res.append(random.nextBoolean()?'1':'0');}String result = res.toString();if(!strSet.contains(result)){return result;}}}}
classSolution{public:
string findDifferentBinaryString(vector<string>& nums){
unordered_set<string>strSet(nums.begin(), nums.end());int n = nums.size();while(true){
string res ="";for(int i =0; i < n; i++){
res +=(rand()%2)?'1':'0';}if(strSet.find(res)== strSet.end()){return res;}}}};
classSolution{/**
* @param {string[]} nums
* @return {string}
*/findDifferentBinaryString(nums){const strSet =newSet(nums);const n = nums.length;while(true){let res = Array.from({length: n },()=>
Math.random()<0.5?'0':'1',).join('');if(!strSet.has(res)){return res;}}}}
publicclassSolution{privateRandom random =newRandom();publicstringFindDifferentBinaryString(string[] nums){HashSet<string> strSet =newHashSet<string>(nums);int n = nums.Length;while(true){StringBuilder res =newStringBuilder();for(int i =0; i < n; i++){
res.Append(random.Next(2)==0?'0':'1');}string result = res.ToString();if(!strSet.Contains(result)){return result;}}}}
funcfindDifferentBinaryString(nums []string)string{
strSet :=make(map[string]bool)for_, s :=range nums {
strSet[s]=true}
n :=len(nums)for{
res :=make([]byte, n)for i :=0; i < n; i++{if rand.Intn(2)==0{
res[i]='0'}else{
res[i]='1'}}
result :=string(res)if!strSet[result]{return result
}}}
class Solution {funfindDifferentBinaryString(nums: Array<String>): String {val strSet = nums.toHashSet()val n = nums.size
val random = java.util.Random()while(true){val res =StringBuilder()for(i in0 until n){
res.append(if(random.nextBoolean())'1'else'0')}val result = res.toString()if(result !in strSet){return result
}}}}
classSolution{funcfindDifferentBinaryString(_ nums:[String])->String{let strSet =Set(nums)let n = nums.count
whiletrue{var res =""for_in0..<n {
res +=Bool.random()?"1":"0"}if!strSet.contains(res){return res
}}}}
Time & Space Complexity
Time complexity: O(∞) in worst case.
Space complexity: O(n)
5. Trie
Intuition
A Trie (prefix tree) stores all input strings and allows us to find a missing string by traversing the tree. At each node, if one of the two children (0 or 1) is missing, we can take that path and fill the rest arbitrarily. This finds a missing string in O(n) time after O(n^2) preprocessing.
Algorithm
Build a Trie by inserting all input strings.
Traverse the Trie from the root:
At each node, check if the '0' or '1' child is missing.
If '0' is missing, append '0' and return (fill remaining with any character).
If '1' is missing, append '1' and return.
If both exist, prefer '1' and continue deeper.
Pad the result with '1's to reach length n if needed.
classNode:def__init__(self):
self.children =[None,None]defcontains_bit(self, bit:int)->bool:return self.children[bit]isnotNonedefput(self, bit:int):
self.children[bit]= Node()defget(self, bit:int):return self.children[bit]classTrie:def__init__(self):
self.root = Node()definsert(self, s:str):
curr = self.root
for c in s:
bit =int(c)ifnot curr.contains_bit(bit):
curr.put(bit)
curr = curr.get(bit)defsearch(self, res:str, curr)->bool:while curr.contains_bit(0)or curr.contains_bit(1):ifnot curr.contains_bit(0):
res.append('0')returnTrueifnot curr.contains_bit(1):
res.append('1')returnTrue
res.append('1')
curr = curr.get(1)returnFalseclassSolution:deffindDifferentBinaryString(self, nums: List[str])->str:
trie = Trie()for s in nums:
trie.insert(s)
res =[]
trie.search(res, trie.root)whilelen(res)<len(nums):
res.append('1')return''.join(res)
classNode{Node[] children;Node(){this.children =newNode[2];}booleancontainsBit(int bit){returnthis.children[bit]!=null;}voidput(int bit){this.children[bit]=newNode();}Nodeget(int bit){returnthis.children[bit];}}classTrie{Node root;Trie(){this.root =newNode();}voidinsert(String s){Node curr = root;for(char c : s.toCharArray()){int bit = c -'0';if(!curr.containsBit(bit)){
curr.put(bit);}
curr = curr.get(bit);}}booleansearch(StringBuilder res,Node curr){while(curr.containsBit(0)|| curr.containsBit(1)){if(!curr.containsBit(0)){
res.append('0');returntrue;}if(!curr.containsBit(1)){
res.append('1');returntrue;}
res.append('1');
curr = curr.get(1);}returnfalse;}}publicclassSolution{publicStringfindDifferentBinaryString(String[] nums){Trie trie =newTrie();for(String s : nums){
trie.insert(s);}StringBuilder res =newStringBuilder();
trie.search(res, trie.root);while(res.length()< nums.length){
res.append('1');}return res.toString();}}
classNode{public:
Node *children[2];Node(){this->children[0]=nullptr;this->children[1]=nullptr;}boolcontainsBit(int bit){returnthis->children[bit]!=nullptr;}voidput(int bit){this->children[bit]=newNode();}
Node*get(int bit){returnthis->children[bit];}};classTrie{public:
Node* root;Trie(){this->root =newNode();}voidinsert(const string& s){
Node* curr = root;for(char c : s){int bit = c -'0';if(!curr->containsBit(bit)){
curr->put(bit);}
curr = curr->get(bit);}}boolsearch(string& res, Node* curr){while(curr->containsBit(0)|| curr->containsBit(1)){if(!curr->containsBit(0)){
res +='0';returntrue;}if(!curr->containsBit(1)){
res +='1';returntrue;}
res +='1';
curr = curr->get(1);}returnfalse;}};classSolution{public:
string findDifferentBinaryString(vector<string>& nums){
Trie trie;for(const string& s : nums){
trie.insert(s);}
string res;
trie.search(res, trie.root);while(res.length()< nums.size()){
res +='1';}return res;}};
classNode{constructor(){this.children =[null,null];}/**
* @param {number} bit
* @return {boolean}
*/containsBit(bit){returnthis.children[bit]!==null;}/**
* @param {number} bit
*/put(bit){this.children[bit]=newNode();}/**
* @param {number} bit
* @return {Node}
*/get(bit){returnthis.children[bit];}}classTrie{constructor(){this.root =newNode();}/**
* @param {string} s
*/insert(s){let curr =this.root;for(const c of s){let bit = c ==='1'?1:0;if(!curr.containsBit(bit)){
curr.put(bit);}
curr = curr.get(bit);}}/**
* @param {string[]} res
* @param {Node} curr
* @return {boolean}
*/search(res, curr){while(curr.containsBit(0)|| curr.containsBit(1)){if(!curr.containsBit(0)){
res.push('0');returntrue;}if(!curr.containsBit(1)){
res.push('1');returntrue;}
res.push('1');
curr = curr.get(1);}returnfalse;}}classSolution{/**
* @param {string[]} nums
* @return {string}
*/findDifferentBinaryString(nums){let trie =newTrie();for(const s of nums){
trie.insert(s);}let res =[];
trie.search(res, trie.root);while(res.length < nums.length){
res.push('1');}return res.join('');}}
publicclassNode{publicNode[] children =newNode[2];publicboolContainsBit(int bit){return children[bit]!=null;}publicvoidPut(int bit){
children[bit]=newNode();}publicNodeGet(int bit){return children[bit];}}publicclassTrie{publicNode root =newNode();publicvoidInsert(string s){Node curr = root;foreach(char c in s){int bit = c -'0';if(!curr.ContainsBit(bit)){
curr.Put(bit);}
curr = curr.Get(bit);}}publicboolSearch(StringBuilder res,Node curr){while(curr.ContainsBit(0)|| curr.ContainsBit(1)){if(!curr.ContainsBit(0)){
res.Append('0');returntrue;}if(!curr.ContainsBit(1)){
res.Append('1');returntrue;}
res.Append('1');
curr = curr.Get(1);}returnfalse;}}publicclassSolution{publicstringFindDifferentBinaryString(string[] nums){Trie trie =newTrie();foreach(string s in nums){
trie.Insert(s);}StringBuilder res =newStringBuilder();
trie.Search(res, trie.root);while(res.Length < nums.Length){
res.Append('1');}return res.ToString();}}
type Node struct{
children [2]*Node
}func(n *Node)containsBit(bit int)bool{return n.children[bit]!=nil}func(n *Node)put(bit int){
n.children[bit]=&Node{}}func(n *Node)get(bit int)*Node {return n.children[bit]}type Trie struct{
root *Node
}funcnewTrie()*Trie {return&Trie{root:&Node{}}}func(t *Trie)insert(s string){
curr := t.root
for_, c :=range s {
bit :=int(c -'0')if!curr.containsBit(bit){
curr.put(bit)}
curr = curr.get(bit)}}func(t *Trie)search(res *[]byte, curr *Node)bool{for curr.containsBit(0)|| curr.containsBit(1){if!curr.containsBit(0){*res =append(*res,'0')returntrue}if!curr.containsBit(1){*res =append(*res,'1')returntrue}*res =append(*res,'1')
curr = curr.get(1)}returnfalse}funcfindDifferentBinaryString(nums []string)string{
trie :=newTrie()for_, s :=range nums {
trie.insert(s)}
res :=[]byte{}
trie.search(&res, trie.root)forlen(res)<len(nums){
res =append(res,'1')}returnstring(res)}
class Node {val children = arrayOfNulls<Node>(2)funcontainsBit(bit: Int): Boolean = children[bit]!=nullfunput(bit: Int){
children[bit]=Node()}funget(bit: Int): Node?= children[bit]}class Trie {val root =Node()funinsert(s: String){var curr = root
for(c in s){val bit = c -'0'if(!curr.containsBit(bit)){
curr.put(bit)}
curr = curr.get(bit)!!}}funsearch(res: StringBuilder, curr: Node): Boolean {var node = curr
while(node.containsBit(0)|| node.containsBit(1)){if(!node.containsBit(0)){
res.append('0')returntrue}if(!node.containsBit(1)){
res.append('1')returntrue}
res.append('1')
node = node.get(1)!!}returnfalse}}class Solution {funfindDifferentBinaryString(nums: Array<String>): String {val trie =Trie()for(s in nums){
trie.insert(s)}val res =StringBuilder()
trie.search(res, trie.root)while(res.length < nums.size){
res.append('1')}return res.toString()}}
classNode{var children:[Node?]=[nil,nil]funccontainsBit(_ bit:Int)->Bool{return children[bit]!=nil}funcput(_ bit:Int){
children[bit]=Node()}funcget(_ bit:Int)->Node?{return children[bit]}}classTrie{var root =Node()funcinsert(_ s:String){var curr = root
for c in s {let bit = c =="1"?1:0if!curr.containsBit(bit){
curr.put(bit)}
curr = curr.get(bit)!}}funcsearch(_ res:inout[Character],_ curr:Node)->Bool{var node = curr
while node.containsBit(0)|| node.containsBit(1){if!node.containsBit(0){
res.append("0")returntrue}if!node.containsBit(1){
res.append("1")returntrue}
res.append("1")
node = node.get(1)!}returnfalse}}classSolution{funcfindDifferentBinaryString(_ nums:[String])->String{let trie =Trie()for s in nums {
trie.insert(s)}var res =[Character]()_= trie.search(&res, trie.root)while res.count < nums.count {
res.append("1")}returnString(res)}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(n2)
Common Pitfalls
Iterating Through All 2^n Possibilities
Since there are only n input strings but 2^n possible strings of length n, a missing string is guaranteed to exist. However, iterating through all 2^n possibilities is exponential and will time out for larger n. The optimal approach only needs to check n+1 candidates or use Cantor's diagonal argument.
Forgetting to Pad Binary Strings with Leading Zeros
When converting integers to binary strings, the result may be shorter than n characters. Forgetting to pad with leading zeros (e.g., "1" instead of "001" for n=3) will cause the generated string to have incorrect length and fail comparison with input strings.
Misunderstanding Cantor's Diagonal Argument
The diagonal approach flips the i-th character of the i-th string to guarantee the result differs from every input. A common mistake is flipping at fixed positions (like always the first character) or not understanding why the diagonal specifically ensures uniqueness.