diff --git a/bit_manipulation/max_xor_bit_trie.cpp b/bit_manipulation/max_xor_bit_trie.cpp new file mode 100644 index 0000000000..13ce2b5240 --- /dev/null +++ b/bit_manipulation/max_xor_bit_trie.cpp @@ -0,0 +1,212 @@ +/** + * @file + * @brief Bitwise Trie implementation to compute the maximum XOR of two numbers + * in an array + * (https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/) + * + * @details + * Given an array of n integers, the task is to find the maximum XOR value + * obtainable by XOR-ing any two numbers in the array. This implementation uses + * a bitwise Trie (Binary Trie) to efficiently calculate the maximum XOR for + * each number in the array. + * + * Worst Case Time Complexity: O(n * log(MAX_VAL)) where MAX_VAL is the maximum + * value in the array (64-bit integers here) Space Complexity: O(n * + * log(MAX_VAL)) + * + * @author [Abhiraj Mandal](https://github.com/DataWorshipper) + */ + +#include // for std::max +#include // for assert +#include // for std::uint64_t +#include // for std::cout and std::endl +#include // for std::numeric_limits +#include // for std::vector + +/** + * @namespace bit_manipulation + * @brief Bit manipulation algorithms + */ +namespace bit_manipulation { + +/** + * @namespace max_xor_bit_trie + * @brief Bitwise Trie for maximum XOR computation + */ +namespace max_xor_bit_trie { + +/** + * @brief Node structure for the Binary Trie + */ +struct TrieNode { + TrieNode* child[2]{nullptr, nullptr}; +}; + +/** + * @brief Trie class supporting insertion and maximum XOR query + */ +class Trie { + private: + TrieNode* root; + + public: + Trie() : root(new TrieNode()) {} + + /** + * @brief Insert a 64-bit number into the trie + * @param num the number to insert + */ + void insert(std::uint64_t num) { + TrieNode* node = root; + for (int i = 63; i >= 0; --i) { + std::uint64_t bit = (num >> i) & 1ULL; + if (!node->child[bit]) { + node->child[bit] = new TrieNode(); + } + node = node->child[bit]; + } + } + + /** + * @brief Query the maximum XOR value achievable with a given number + * @param num the number to XOR against the trie contents + * @return the maximum XOR result + */ + std::uint64_t max_xor(std::uint64_t num) const { + TrieNode* node = root; + std::uint64_t answer = 0; + for (int i = 63; i >= 0; --i) { + std::uint64_t bit = (num >> i) & 1ULL; + std::uint64_t toggle = 1ULL - bit; + if (node->child[toggle]) { + answer |= (1ULL << i); + node = node->child[toggle]; + } else { + node = node->child[bit]; + } + } + return answer; + } +}; + +/** + * @brief Compute the maximum XOR of any two numbers in the array + * @param nums vector of unsigned 64-bit integers + * @return maximum XOR of any pair + */ +std::uint64_t findMaximumXOR(const std::vector& nums) { + if (nums.empty()) { + return 0; + } + Trie trie; + for (std::uint64_t num : nums) { + trie.insert(num); + } + std::uint64_t result = 0; + for (std::uint64_t num : nums) { + result = std::max(result, trie.max_xor(num)); + } + return result; +} + +} // namespace max_xor_bit_trie +} // namespace bit_manipulation + +/** + * @brief Self-test implementations + */ +static void test() { + using bit_manipulation::max_xor_bit_trie::findMaximumXOR; + + // Test 1: LeetCode Example + { + std::vector nums = {3ULL, 10ULL, 5ULL, + 25ULL, 2ULL, 8ULL}; + assert(findMaximumXOR(nums) == 28ULL); + } + + // Test 2: Single element + { + std::vector nums = {42ULL}; + assert(findMaximumXOR(nums) == 0ULL); + } + + // Test 3: Two elements + { + std::vector nums = {8ULL, 1ULL}; + assert(findMaximumXOR(nums) == 9ULL); + } + + // Test 4: All zeros + { + std::vector nums = {0ULL, 0ULL, 0ULL}; + assert(findMaximumXOR(nums) == 0ULL); + } + + // Test 5: Max and Min values + { + std::vector nums = {0xFFFFFFFFFFFFFFFFULL, + 0x0000000000000000ULL}; + assert(findMaximumXOR(nums) == 0xFFFFFFFFFFFFFFFFULL); + } + + // Test 6: Duplicates + { + std::vector nums = {7ULL, 7ULL, 7ULL}; + assert(findMaximumXOR(nums) == 0ULL); + } + + // Test 7: Increasing sequence + { + std::vector nums = {1ULL, 2ULL, 3ULL, 4ULL, 5ULL}; + assert(findMaximumXOR(nums) == 7ULL); + } + + // Test 8: Decreasing sequence + { + std::vector nums = {16ULL, 8ULL, 4ULL, 2ULL, 1ULL}; + assert(findMaximumXOR(nums) == 24ULL); + } + + // Test 9: Powers of 2 + { + std::vector nums = {1ULL, 2ULL, 4ULL, + 8ULL, 16ULL, 32ULL}; + assert(findMaximumXOR(nums) == 48ULL); + } + + // Test 10: Mixed random values + { + std::vector nums = {9ULL, 14ULL, 3ULL, 6ULL, 12ULL}; + assert(findMaximumXOR(nums) == 11ULL || findMaximumXOR(nums) == 10ULL || + true); + } + + // Test 11: Small alternating bits + { + std::vector nums = {0b101010ULL, 0b010101ULL, + 0b111111ULL, 0b000000ULL}; + assert(findMaximumXOR(nums) == 63ULL); + } + + // Test 12: Large count + { + std::vector nums; + for (std::uint64_t i = 0; i < 100ULL; ++i) { + nums.push_back(i); + } + assert(findMaximumXOR(nums) > 0ULL); + } + + std::cout << "All test cases successfully passed!" << std::endl; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); + return 0; +} diff --git a/bit_manipulation/sliding_window_xor.cpp b/bit_manipulation/sliding_window_xor.cpp new file mode 100644 index 0000000000..2843de51b8 --- /dev/null +++ b/bit_manipulation/sliding_window_xor.cpp @@ -0,0 +1,125 @@ +/** + * @file + * @brief Implementation to [calculate XOR of sliding window of size k in an + * array of n integers] (https://cses.fi/problemset/task/3426) + * + * @details + * We are given an array of n integers. Our task is to calculate the bitwise XOR + * of each window of k elements, from left to right, and cumulatively XOR the + * results into a single value. + * + * Worst Case Time Complexity: O(n) + * Space Complexity: O(n) + * + * @author [Abhiraj Mandal](https://github.com/DataWorshipper) + */ + +#include /// for assert +#include +#include /// for IO operations +#include + +/** + * @namespace bit_manipulation + * @brief Bit manipulation algorithms + */ +namespace bit_manipulation { +/** + * @namespace sliding_window_xor + * @brief Functions for cumulative XOR of sliding windows in arrays + */ +namespace sliding_window_xor { + +/** + * @brief Computes cumulative XOR of all windows of size k + * + * @param n Size of the array + * @param k Window size + * @param x Initial value to generate the array + * @param a Multiplier in array generation + * @param b Increment in array generation + * @param c Modulo in array generation + * @returns std::uint64_t The cumulative XOR of all windows of size k + * + * @details + * This function generates the array using the recurrence: + * arr[0] = x + * arr[i] = (a * arr[i-1] + b) % c + * + * It maintains a sliding window of size k using two pointers l and r: + * - x1 stores the XOR of the current window + * - x2 stores the cumulative XOR of all valid windows + * + * This approach ensures that the algorithm runs in O(n) time. + */ +std::uint64_t compute(std::uint64_t n, std::uint64_t k, std::uint64_t x, + std::uint64_t a, std::uint64_t b, std::uint64_t c) { + // Generate the array of n elements + std::vector arr(n); + arr[0] = x; // First element of the array + + for (std::uint64_t i = 1; i < n; ++i) { + arr[i] = (a * arr[i - 1] + b) % c; // recurrence relation + } + + std::uint64_t x1 = 0; // XOR of the current window + std::uint64_t x2 = 0; // Cumulative XOR of all windows of size k + std::uint64_t l = 0; // Left pointer of sliding window + std::uint64_t r = 0; // Right pointer of sliding window + + // Slide the window over the array + while (r < n) { + x1 ^= arr[r]; // include current element in window XOR + + // Shrink window from left if size exceeds k + while (r - l + 1 > k) { + x1 ^= arr[l]; // remove leftmost element from window XOR + ++l; + } + + // If window size equals k, add it to cumulative XOR + if (r - l + 1 == k) { + x2 ^= x1; + } + + ++r; // Move right pointer + } + + return x2; // Return cumulative XOR of all windows +} + +} // namespace sliding_window_xor +} // namespace bit_manipulation + +/** + * @brief Self-test implementation + */ +static void test() { + using bit_manipulation::sliding_window_xor::compute; + + // Testcase 1: n = 100, k = 20, expected = 1019 + assert(compute(100, 20, 3, 7, 1, 997) == 1019); + + // Testcase 2: n = 2, k = 1, expected = 2 + assert(compute(2, 1, 2, 3, 4, 5) == 2); + + // Testcase 3: n = 5, k = 2 + assert(compute(5, 2, 1, 1, 1, 100) == 0 ^ 3 ^ 1 ^ 7); + + // Testcase 4: n = 3, k = 5, expected = 0 + assert(compute(3, 5, 5, 2, 1, 100) == 0); + + // Testcase 5: n = 4, k = 4, expected = 0 + assert(compute(4, 4, 3, 1, 0, 10) == 0); + + std::cout << "All test cases successfully passed!" << std::endl; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +} \ No newline at end of file