You are given a number of queries Q and each query will be of the following types:
- Query 1 : add(x) This means add x into your data structure.
- Query 2 : maxXOR(y) This means print the maximum possible XOR of y with all the elements already stored in the data structure.
1 <= x, y <= 10^9 1 <= 10^5 <= Q The data structure begins with only a 0 in it. Example:
Input: (1 10), (1 13), (2 10), (1 9), (1 5), (2 6) Output: 7 15 Add 10 and 13 to stream. Find maximum XOR with 10, which is 7 Insert 9 and 5 Find maximum XOR with 6 which is 15.
A good way to solve this problem is to use a Trie. A prerequisite for this post is Trie Insert and Search. Each Trie Node will look following:
struct TrieNode { // We use binary and hence we // need only 2 children TrieNode* Children[2]; bool isLeaf; };
Another thing to handle is that we have to pad the binary equivalent of each input number by a suitable number of zeros to the left before storing them. The maximum possible value of x or y is 10^9 and hence 32 bits will be sufficient. So how does this work? Assume we have to insert 3 and 7 into Trie. The Trie starts out with 0 and after these three insertions can be visualized like this:
// C++ program to find maximum XOR in // a stream of integers #include<bits/stdc++.h> using namespace std;
struct TrieNode
{ TrieNode* children[2];
bool isLeaf;
}; // This checks if the ith position in // binary of N is a 1 or a 0 bool check( int N, int i)
{ return ( bool )(N & (1<<i));
} // Create a new Trie node TrieNode* newNode() { TrieNode* temp = new TrieNode;
temp->isLeaf = false ;
temp->children[0] = NULL;
temp->children[1] = NULL;
return temp;
} // Inserts x into the Trie void insert(TrieNode* root, int x)
{ TrieNode* Crawler = root;
// padding upto 32 bits
for ( int i = 31; i >= 0; i--)
{
int f = check(x, i);
if (! Crawler->children[f])
Crawler->children[f] = newNode();
Crawler = Crawler->children[f];
}
Crawler->isLeaf = true ;
} // Finds maximum XOR of x with stream of // elements so far. int query2(TrieNode *root, int x)
{ TrieNode* Crawler = root;
// Do XOR from root to a leaf path
int ans = 0;
for ( int i = 31; i >= 0; i--)
{
// Find i-th bit in x
int f = check(x, i);
// Move to the child whose XOR with f
// is 1.
if ((Crawler->children[f ^ 1]))
{
ans = ans + (1 << i); // update answer
Crawler = Crawler->children[f ^ 1];
}
// If child with XOR 1 doesn't exist
else
Crawler = Crawler->children[f];
}
return ans;
} // Process x (Add x to the stream) void query1(TrieNode *root, int x)
{ insert(root, x);
} // Driver code int main()
{ TrieNode* root = newNode();
query1(root, 10);
query1(root, 13);
cout << query2(root, 10) << endl;
query1(root, 9);
query1(root, 5);
cout << query2(root, 6) << endl;
return 0;
} |
// Java program to find maximum XOR in // a stream of integers import java.util.ArrayList;
import java.util.List;
class TrieNode {
TrieNode[] children = new TrieNode[ 2 ];
boolean isLeaf;
TrieNode()
{
this .isLeaf = false ;
children[ 0 ] = null ;
children[ 1 ] = null ;
}
} public class Main {
// This checks if the ith position in
// binary of N is a 1 or a 0
static boolean check( int N, int i)
{
return (N & ( 1 << i)) != 0 ;
}
// Create a new Trie node
static TrieNode newNode() { return new TrieNode(); }
// Inserts x into the Trie
static void insert(TrieNode root, int x)
{
TrieNode Crawler = root;
// padding upto 32 bits
for ( int i = 31 ; i >= 0 ; i--) {
int f = check(x, i) ? 1 : 0 ;
if (Crawler.children[f] == null ) {
Crawler.children[f] = newNode();
}
Crawler = Crawler.children[f];
}
Crawler.isLeaf = true ;
}
// Finds maximum XOR of x with stream of
// elements so far.
static int query2(TrieNode root, int x)
{
TrieNode Crawler = root;
// Do XOR from root to a leaf path
int ans = 0 ;
for ( int i = 31 ; i >= 0 ; i--) {
// Find i-th bit in x
int f = check(x, i) ? 1 : 0 ;
// Move to the child whose XOR with f
// is 1.
if (Crawler.children[f ^ 1 ] != null ) {
// update answer
ans += ( 1 << i);
Crawler = Crawler.children[f ^ 1 ];
}
// If child with XOR 1 doesn't exist
else {
Crawler = Crawler.children[f];
}
}
return ans;
}
// Process x (Add x to the stream)
static void query1(TrieNode root, int x)
{
insert(root, x);
}
// Driver code
public static void main(String[] args)
{
TrieNode root = newNode();
query1(root, 10 );
query1(root, 13 );
System.out.println(query2(root, 10 ));
query1(root, 9 );
query1(root, 5 );
System.out.println(query2(root, 6 ));
}
} // This code is contributed by Aman Kumar |
# Define TrieNode class class TrieNode:
def __init__( self ):
self .children = [ None , None ]
self .isLeaf = False
# Check if ith bit of N is 1 or 0 def check(N, i):
return (N & ( 1 << i)) ! = 0
# Create a new TrieNode def newNode():
return TrieNode()
# Insert x into Trie def insert(root, x):
crawler = root
# Padding up to 32 bits
for i in range ( 31 , - 1 , - 1 ):
f = 1 if check(x, i) else 0
if crawler.children[f] is None :
crawler.children[f] = newNode()
crawler = crawler.children[f]
crawler.isLeaf = True
return root
# Find maximum XOR of x with stream of elements so far def query2(root, x):
crawler = root
# Do XOR from root to a leaf path
ans = 0
for i in range ( 31 , - 1 , - 1 ):
# Find ith bit in x
f = 1 if check(x, i) else 0
# Move to the child whose XOR with f is 1
if crawler.children[f ^ 1 ] is not None :
# Update answer
ans + = 1 << i
crawler = crawler.children[f ^ 1 ]
# If child with XOR 1 doesn't exist
else :
crawler = crawler.children[f]
return ans
# Process x (add x to the stream) def query1(root, x):
return insert(root, x)
# Driver code root = TrieNode()
root = query1(root, 10 )
root = query1(root, 13 )
print (query2(root, 10 ))
root = query1(root, 9 )
root = query1(root, 5 )
print (query2(root, 6 ))
|
// C# program to find maximum XOR in // a stream of integers using System;
public class TrieNode {
public TrieNode[] children = new TrieNode[2];
public bool isLeaf;
public TrieNode()
{ this .isLeaf = false ;
children[0] = null ;
children[1] = null ;
} } public class Program {
// This checks if the ith position in // binary of N is a 1 or a 0 static bool Check( int N, int i)
{ return (N & (1 << i)) != 0;
} // Create a new Trie node static TrieNode NewNode() { return new TrieNode(); }
// Inserts x into the Trie static void insert(TrieNode root, int x)
{ TrieNode Crawler = root;
// padding upto 32 bits
for ( int i = 31; i >= 0; i--)
{
int f = Check(x, i) ? 1 : 0;
if (Crawler.children[f] == null )
{
Crawler.children[f] = NewNode();
}
Crawler = Crawler.children[f];
}
Crawler.isLeaf = true ;
} // Finds maximum XOR of x with stream of // elements so far. static int query2(TrieNode root, int x)
{ TrieNode Crawler = root;
// Do XOR from root to a leaf path
int ans = 0;
for ( int i = 31; i >= 0; i--)
{
// Find i-th bit in x
int f = Check(x, i) ? 1 : 0;
// Move to the child whose XOR with f
// is 1.
if (Crawler.children[f ^ 1] != null )
{
// update answer
ans += (1 << i);
Crawler = Crawler.children[f ^ 1];
}
// If child with XOR 1 doesn't exist
else
{
Crawler = Crawler.children[f];
}
}
return ans;
} // Process x (Add x to the stream) static void Query1(TrieNode root, int x)
{ insert(root, x);
} // Driver code public static void Main()
{ TrieNode root = NewNode();
Query1(root, 10);
Query1(root, 13);
Console.WriteLine(query2(root, 10));
Query1(root, 9);
Query1(root, 5);
Console.WriteLine(query2(root, 6));
} } // This code is contributed by Pushpesh Raj. |
// Define TrieNode class class TrieNode { constructor() { this .children = [ null , null ];
this .isLeaf = false ;
} } // Check if ith bit of N is 1 or 0 function check(N, i) {
return (N & (1 << i)) !== 0;
} // Create a new TrieNode function newNode() {
return new TrieNode();
} // Insert x into Trie function insert(root, x) {
let crawler = root; // Padding up to 32 bits for (let i = 31; i >= 0; i--) {
const f = check(x, i) ? 1 : 0; if (crawler.children[f] === null ) {
crawler.children[f] = newNode(); } crawler = crawler.children[f]; } crawler.isLeaf = true ;
return root;
} // Find maximum XOR of x with stream of elements so far function query2(root, x) {
let crawler = root; // Do XOR from root to a leaf path let ans = 0; for (let i = 31; i >= 0; i--) {
// Find ith bit in x const f = check(x, i) ? 1 : 0; // Move to the child whose XOR with f is 1 if (crawler.children[f ^ 1] !== null ) {
// Update answer
ans += 1 << i;
crawler = crawler.children[f ^ 1];
} // If child with XOR 1 doesn't exist else {
crawler = crawler.children[f];
} } return ans;
} // Process x (add x to the stream) function query1(root, x) {
return insert(root, x);
} // Driver code let root = new TrieNode();
root = query1(root, 10); root = query1(root, 13); document.write(query2(root, 10)); root = query1(root, 9); root = query1(root, 5); document.write(query2(root, 6)); |
7 15
The space taken by the Trie is O(n*log(n)). Each query of type 1 takes O(log(n)) time. Each query of type 2 takes O(log(n)) time too. Here n is the largest query number. Follow up problem: What if we are given three queries instead of two? 1) add(x) This means add x into your data structure (duplicates are allowed). 2) maxXOR(y) This means print the maximum possible XOR of y with all the elements already stored in the data structure. 3) remove(z) This means remove one instance of z from the data structure. What changes in the Trie solution can achieve this?