Search code examples
algorithmxor

Maximum XOR value faster than just using XOR


Given a number N and an array of integers (all nos less than 2^15). (A is size of array 100000)
Find Maximum XOR value of N and a integer from the array.

Q is no of queries (50000) and start, stop is the range in the array.

Input:
A Q
a1 a2 a3 ...
N start stop

Output:
Maximum XOR value of N and an integer in the array with the range specified.

Eg: Input
15 2 (2 is no of queries)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
10 6 10 (Query 1)
10 6 10 (Query 2)

Output:
13
13

Code:

for(int i=start-1;i<stop;i++){
int t =no[i]^a;
if(maxxor<t)
     maxxor=t;
}
cout << maxxor <<endl;

I need a algorithm 10-100 times faster than this. Sorting is too expensive. I have also tried binary trees,bit manipulation.

How about a 2x - 3x improvement?. Is that possible by optimization.


Solution

  • It is possible to develop faster algorithm.

    Let's call bits of N: a[0], a[1], ..., a[15], e.g if N = 13 = 0000000 00001101 (in binary), then a[0] = a[1] = ... a[11] = 0, a[12] = 1, a[13] = 1, a[14] = 0, a[15] = 1.

    The main idea of algorithm is following: If a[0] == 1, then best possible answer has this bit zeroed. If a[0] == 0, then best possible answer has one at this position. So at first you check if you have some number with the desired bit. If yes, you should take only number with this bit. If no, you take it's inverse. Then you process other bits in same manner. E.g. if a[0] == 1, a[1] == 0, you first check whether there is number beginning with zero, if yes then you check whether there is a number beginning with 01. If nothing begins with zero, then you check whether there is a number beggining with 11. And so on...

    So you need a fast algorithm to answer following query: Is there a number beginning with bits ... in range start, stop?

    One possibility: Constuct trie from binary representation of numbers. In each node store all positions where this prefix is in array (and sort them). Then answering to this query can be a simple walk through this trie. To check whether there is suitable prefix in start, stop range you should do a binary search over stored array in a node.

    This could lead to algorithm with complexity O(lg^2 N) which is faster.

    Here is the code, it hasn't been tested much, may contain bugs:

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    class TrieNode {
     public:
      TrieNode* next[2];
      vector<int> positions;
    
      TrieNode() {
        next[0] = next[1] = NULL;
      }
    
      bool HasNumberInRange(int start, int stop) {
        vector<int>::iterator it = lower_bound(
            positions.begin(), positions.end(), start);
        if (it == positions.end()) return false;
        return *it < stop;
      }
    };
    
    void AddNumberToTrie(int number, int index, TrieNode* base) {
      TrieNode* cur = base;
      // Go through all binary digits from most significant
      for (int i = 14; i >= 0; i--) {
        int digit = 0;
        if ((number & (1 << i)) != 0) digit = 1;
        cur->positions.push_back(index);
        if (cur->next[digit] == NULL) {
          cur->next[digit] = new TrieNode;
        }
        cur = cur->next[digit];
      }
      cur->positions.push_back(index);
    }
    
    int FindBestNumber(int a, int start, int stop, TrieNode* base) {
      int best_num = 0;
      TrieNode* cur = base;
      for (int i = 14; i >= 0; i--) {
        int digit = 1;
        if ((a & (1 << i)) != 0) digit = 0;
        if (cur->next[digit] == NULL || 
            !cur->next[digit]->HasNumberInRange(start, stop))
          digit = 1 - digit;
        best_num *= 2;
        best_num += digit;
        cur = cur->next[digit];
      }
      return best_num;
    }
    
    
    int main() {
      int n; scanf("%d", &n);
      int q; scanf("%d", &q);
      TrieNode base;
      for (int i = 0; i < n; i++) {
        int x; scanf("%d", &x);
        AddNumberToTrie(x, i, &base);
      }
    
      for (int i = 0; i < q; i++) {
        int a, start, stop;
        // Finds biggest i, such that start <= i < stop and XOR with a is as big as possible
        // Base index is 0
        scanf("%d %d %d", &a, &start, &stop);
        printf("%d\n", FindBestNumber(a, start, stop, &base)^a);
      }
    }