Find maximum XOR of given integer in a stream of integers
Last Updated :
21 Mar, 2023
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: For simplification, the padding has been done to store each number using 3 bits. Note that in binary: 3 is 011 7 is 111 Now if we have to insert 1 into our Trie, we can note that 1 is 001 and we already have path for 00. So we make a new node for the last set bit and after connecting, we get this: Now if we have to take XOR with 5 which is 101, we note that for the leftmost bit (position 2), we can choose a 0 starting at the root and thus we go to the left. This is the position 2 and we add 2^2 to the answer. For position 1, we have a 0 in 5 and we see that we can choose a 1 from our current node. Thus we go right and add 2^1 to the answer. For position 0, we have a 1 in 5 and we see that we cannot choose a 0 from our current node, thus we go right. The path taken for 5 is shown above. The answer is thus 2^2 + 2^1 = 6.
CPP
#include<bits/stdc++.h>
using namespace std;
struct TrieNode
{
TrieNode* children[2];
bool isLeaf;
};
bool check( int N, int i)
{
return ( bool )(N & (1<<i));
}
TrieNode* newNode()
{
TrieNode* temp = new TrieNode;
temp->isLeaf = false ;
temp->children[0] = NULL;
temp->children[1] = NULL;
return temp;
}
void insert(TrieNode* root, int x)
{
TrieNode* Crawler = root;
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 ;
}
int query2(TrieNode *root, int x)
{
TrieNode* Crawler = root;
int ans = 0;
for ( int i = 31; i >= 0; i--)
{
int f = check(x, i);
if ((Crawler->children[f ^ 1]))
{
ans = ans + (1 << i);
Crawler = Crawler->children[f ^ 1];
}
else
Crawler = Crawler->children[f];
}
return ans;
}
void query1(TrieNode *root, int x)
{
insert(root, x);
}
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
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 {
static boolean check( int N, int i)
{
return (N & ( 1 << i)) != 0 ;
}
static TrieNode newNode() { return new TrieNode(); }
static void insert(TrieNode root, int x)
{
TrieNode Crawler = root;
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 ;
}
static int query2(TrieNode root, int x)
{
TrieNode Crawler = root;
int ans = 0 ;
for ( int i = 31 ; i >= 0 ; i--) {
int f = check(x, i) ? 1 : 0 ;
if (Crawler.children[f ^ 1 ] != null ) {
ans += ( 1 << i);
Crawler = Crawler.children[f ^ 1 ];
}
else {
Crawler = Crawler.children[f];
}
}
return ans;
}
static void query1(TrieNode root, int x)
{
insert(root, x);
}
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 ));
}
}
|
Python3
class TrieNode:
def __init__( self ):
self .children = [ None , None ]
self .isLeaf = False
def check(N, i):
return (N & ( 1 << i)) ! = 0
def newNode():
return TrieNode()
def insert(root, x):
crawler = root
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
def query2(root, x):
crawler = root
ans = 0
for i in range ( 31 , - 1 , - 1 ):
f = 1 if check(x, i) else 0
if crawler.children[f ^ 1 ] is not None :
ans + = 1 << i
crawler = crawler.children[f ^ 1 ]
else :
crawler = crawler.children[f]
return ans
def query1(root, x):
return insert(root, x)
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#
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 {
static bool Check( int N, int i)
{
return (N & (1 << i)) != 0;
}
static TrieNode NewNode() { return new TrieNode(); }
static void insert(TrieNode root, int x)
{
TrieNode Crawler = root;
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 ;
}
static int query2(TrieNode root, int x)
{
TrieNode Crawler = root;
int ans = 0;
for ( int i = 31; i >= 0; i--)
{
int f = Check(x, i) ? 1 : 0;
if (Crawler.children[f ^ 1] != null )
{
ans += (1 << i);
Crawler = Crawler.children[f ^ 1];
}
else
{
Crawler = Crawler.children[f];
}
}
return ans;
}
static void Query1(TrieNode root, int x)
{
insert(root, x);
}
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));
}
}
|
Javascript
class TrieNode {
constructor() {
this .children = [ null , null ];
this .isLeaf = false ;
}
}
function check(N, i) {
return (N & (1 << i)) !== 0;
}
function newNode() {
return new TrieNode();
}
function insert(root, x) {
let crawler = root;
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;
}
function query2(root, x) {
let crawler = root;
let ans = 0;
for (let i = 31; i >= 0; i--) {
const f = check(x, i) ? 1 : 0;
if (crawler.children[f ^ 1] !== null ) {
ans += 1 << i;
crawler = crawler.children[f ^ 1];
}
else {
crawler = crawler.children[f];
}
}
return ans;
}
function query1(root, x) {
return insert(root, x);
}
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?
Like Article
Suggest improvement
Share your thoughts in the comments
Please Login to comment...