5696. 统计异或值在范围内的数对有多少

5696. 统计异或值在范围内的数对有多少

难度困难3收藏分享切换为英文接收动态反馈

给你一个整数数组 nums (下标 从 0 开始 计数)以及两个整数:lowhigh ,请返回 漂亮数对 的数目。

漂亮数对 是一个形如 (i, j) 的数对,其中 0 <= i < j < nums.lengthlow <= (nums[i] XOR nums[j]) <= high

示例 1:

输入:nums = [1,4,2,7], low = 2, high = 6
输出:6
解释:所有漂亮数对 (i, j) 列出如下:
    - (0, 1): nums[0] XOR nums[1] = 5 
    - (0, 2): nums[0] XOR nums[2] = 3
    - (0, 3): nums[0] XOR nums[3] = 6
    - (1, 2): nums[1] XOR nums[2] = 6
    - (1, 3): nums[1] XOR nums[3] = 3
    - (2, 3): nums[2] XOR nums[3] = 5

示例 2:

输入:nums = [9,8,4,2,1], low = 5, high = 14
输出:8
解释:所有漂亮数对 (i, j) 列出如下:
    - (0, 2): nums[0] XOR nums[2] = 13
    - (0, 3): nums[0] XOR nums[3] = 11
    - (0, 4): nums[0] XOR nums[4] = 8
    - (1, 2): nums[1] XOR nums[2] = 12
    - (1, 3): nums[1] XOR nums[3] = 10
    - (1, 4): nums[1] XOR nums[4] = 9
    - (2, 3): nums[2] XOR nums[3] = 6
    - (2, 4): nums[2] XOR nums[4] = 5

提示:

  • 1 <= nums.length <= 2e4
  • 1 <= nums[i] <= 2e4
  • 1 <= low <= high <= 2e4

解决方案:

利用字典树统计二进制前缀相同的数字个数。

如:

\[1010\\ 1001\\ 0100\\ 1011\\ \]

上面四个二进制数字中,前缀为 \(1\) 的数字有 \(3\) 个;前缀为 \(101\) 的数字有 \(2\) 个,前缀为 \(0100\) 的数字有一个。

在树中的存储方式如下:

从根节点到叶子节点的路径代表一个二进制数字,结点上的值代表二进制前缀相同的数字个数。

指针实现 trie:

class Trie {
    int cnt;
    Trie *son[2];
public:
    Trie():cnt(0) {
        son[0] = son[1] = NULL;
    }
    void insert(int x) {
        Trie *p = this;
        for (int i = 15; i >= 0; --i) {
            int t = x>>i&1;
            if (!p->son[t]) p->son[t] = new Trie;
            p = p->son[t];
            ++p->cnt;
        }
    }
    int query(int x, int val) {  // 寻找与x异或后大于val的数的个数
        Trie *p = this;
        int ret = 0;
        for (int i = 15; i >= 0; --i) {
            int a = x>>i&1, b = val>>i&1;
            if (b == 0) {
                if (p->son[a^1]) ret += p->son[a^1]->cnt;
                p = p->son[a];
            }
            else {
                p = p->son[a^1];
            }
            if (!p) return ret;
        }
        return ret;
    }
};

class Solution {
public:
    int countPairs(vector<int>& nums, int low, int high) {
        Trie trie;
        for (int num : nums) trie.insert(num);
        int ans = 0;
        for (int num : nums) {
            ans += trie.query(num, low-1) - trie.query(num, high);
            //cout << ans << '#';
        }
        //cout << endl;
        return ans>>1;
    }
};

数组实现trie:

const int N = 1e5;
int tree[N][2], cnt[N];
class Trie {
    int tot = 0;
public:
    Trie() {memset(tree, 0, sizeof tree), memset(cnt,0,sizeof cnt);}
    void insert(int x) {
        int p = 0;
        for (int i = 15; i >= 0; --i) {
            int t = x>>i&1;
            if (!tree[p][t]) tree[p][t] = ++tot;
            p = tree[p][t];
            ++cnt[p];
            //cout << i << '-' << t << ' ' << cnt[p] << endl;
        }
    }
    int query(int x, int val) {  // 寻找与x异或后大于val的数的个数
        int p = 0;
        int ret = 0;
        for (int i = 15; i >= 0; --i) {
            int a = x>>i&1, b = val>>i&1;
            if (b == 0) {
                ret += cnt[tree[p][a^1]];
                p = tree[p][a];
            }
            else {
                p = tree[p][a^1];
            }
            if (p == 0) return ret;
        }
        return ret;
    }
};

class Solution {
public:
    int countPairs(vector<int>& nums, int low, int high) {
        Trie trie;
        for (int num : nums) trie.insert(num);
        int ans = 0;
        for (int num : nums) {
            ans += trie.query(num, low-1) - trie.query(num, high);
            //cout << ans << '#';
        }
        //cout << endl;
        return ans>>1;
    }
};
posted @ 2021-03-21 14:40  Zewbie  阅读(49)  评论(0编辑  收藏  举报