본문 바로가기

Problem Solving/Leet Code

1707 Maximum XOR With an Element From Array

이번 문제는 Trie 구조를 이용해서 푸는 문제인데, Trie로 푸는 알고리즘 문제는 처음이었고,

그래서 풀 때, 시간이 좀 걸렸었다.

문제는 nums 배열에서 Queries 배열의 각 query의 두 번째 원소보다 같거나 작은 원소들과

첫 번째 원소와 XOR 연산을 했을 때의 최댓값을 찾는 문제이다.

 

어떠한 수 n과 임의의 수에 대해 XOR연산을 할 때, 만약 임의의 수가 5(101)와 4(100)라면, 공통되는 부분인 

10의 계산 결과를 저장할 수만 있다면, 2번 계산하지 않아도 될 것이다.

이 문제에서는 XOR연산의 이러한 점을 이용하여 비트 레벨에서 Trie구조를 만들어 문제를 풀 것이다.

 

일단 우리가 짤 Trie는 root가 상위 비트, child가 하위 비트인 Trie이다. 그래서 queries는 두 번째 원소가 낮은 원소인 query를 우선하여 정렬하고, nums 또한 정렬하여 추후 Trie를 구성할 때, Trie가 queries를 순회하면서 Trie에 저장된 원소의 크기가 점점 커질 수 있도록 했다. 그리고 추후 queries의 순서에 맞게 결과를 반환하기 위해서 각 query의 인덱스를 추가해주었다.

 

Trie를 구성할 때에는 int의 32비트에서 최우측 비트가 0번째라고 할 때, 부호 비트인 32번째 비트를 제외하고

최상위 비트인 31번째 비트부터 구성하였다. 그리고 가장 큰 XOR 연산의 결과를 찾을 때는 Trie에서 query의

첫 번째 원소의 최상위 비트부터 결과를 찾아나갔다.

 

C++

더보기
class Solution {
    class Trie{
        private:
            Trie* branch[2];
        public:
            Trie(){
                branch[0] = nullptr;
                branch[1] = nullptr;
            }
            static void build_trie(int num, Trie* root){
                Trie* curr = root;
                for(int i = 31; i >= 0; --i){
                    int value = (num >> i) & 1;
                    if (curr -> branch[value] == nullptr)
                        curr -> branch[value] = new Trie();
                    curr = curr -> branch[value];
                }
            }
            static int search(Trie* curr, int num){
                int res = 0;
                for(int i = 31; i >= 0  && curr != nullptr; --i){
                    int value = 1 - (num >> i) & 1;
                    if (curr -> branch[value] != nullptr){
                        res |= (1 << i);
                        curr = curr -> branch[value];
                    }
                    else {
                        curr = curr -> branch[1 - value];
                    }
                }
                return res;
            }
    };
public:
    vector<int> maximizeXor(vector<int>& nums, vector<vector<int>>& queries) {
        sort(nums.begin(), nums.end());
        for(size_t i = 0; i < queries.size(); ++i)
            queries[i].push_back(i);
        sort(queries.begin(), queries.end(), 
             [](vector<int>& a, vector<int>& b){return a[1] < b[1];});
        vector<int> res(queries.size(), -1);
        
        Trie* root = new Trie();
        int num_index = 0;
        for(size_t i = 0; i < queries.size(); ++i){
            while(num_index < nums.size() && nums[num_index] <= queries[i][1]){
                Trie::build_trie(nums[num_index], root);
                ++num_index;
            }
            if (num_index)
                res[queries[i][2]] = Trie::search(root, queries[i][0]);
        }
        
        return res;
    }
};

Python

더보기
class Solution:   
    def maximizeXor(self, nums: List[int], queries: List[List[int]]) -> List[int]:
        class Trie:
            def __init__(self):
                self.branch = {}
            def build_trie(self, num, root):
                curr = root.branch
                for i in range(31,-1,-1):
                    value = (num >> i) & 1
                    if value not in curr:
                        curr[value] = {}
                    curr = curr[value]
                
            def search(self, num, curr):
                res = 0
                if not self.branch:
                    return -1
                curr = self.branch
                res = 0
                for i in range(31,-1,-1):
                    value = 1 - (num >> i) & 1
                    if value in curr:
                        curr = curr[value]
                        res |= (1 << i)
                    else:
                        curr = curr[1 - value]
                return res
        
        res = [ -1 for i in range(len(queries))]
        nums.sort()
        for i in range(len(queries)):
            queries[i].append(i)
        queries.sort(key = lambda x : x[1])
        
        num_index = 0
        root = Trie()
        for i in range(len(queries)):
            while num_index < len(nums) and nums[num_index] <= queries[i][1]:
                root.build_trie(nums[num_index], root)
                num_index += 1
            if num_index:
                res[queries[i][2]] = root.search(queries[i][0], root)
                
        return res
        

처음에 C++코드를 파이썬으로 그대로 이식했는데 시간 초과가 떴다

그래서 Trie의 branch를 리스트에서 딕셔너리로 바꾸어 주었더니 또 잘 됐다.

흠... 리스트 오버헤드가 배열보다 앞도적으로 크거나 그러진 않을 텐데 뭐 때문이지..?

728x90

'Problem Solving > Leet Code' 카테고리의 다른 글

1720 Decode XORed Array  (0) 2021.01.29
239 Sliding Window Maximum  (0) 2021.01.15
1706 Where Will the Ball Fall  (0) 2021.01.11
1705 Maximum Number of Eaten Apples  (0) 2021.01.11
1704 Determine if String Halves Are Alike  (0) 2021.01.11