|
53 | 53 |
|
54 | 54 | <!-- 这里可写通用的实现逻辑 -->
|
55 | 55 |
|
| 56 | +**方法一:0-1 字典树** |
| 57 | + |
| 58 | +对于这种区间 $[low, high]$ 统计的问题,我们可以考虑将其转换为统计 $[0, high]$ 和 $[0, low - 1]$ 的问题,然后相减即可得到答案。 |
| 59 | + |
| 60 | +在这道题中,我们可以统计有多少数对的异或值小于 $high+1$,然后再统计有多少数对的异或值小于 $low$,相减的结果就是异或值在区间 $[low, high]$ 之间的数对数量。 |
| 61 | + |
| 62 | +另外,对于数组异或计数问题,我们通常可以使用“0-1 字典树”来解决。 |
| 63 | + |
| 64 | +在字典树中,我们定义两个函数,一个是 $insert(x)$,表示将数 $x$ 插入到字典树中;另一个是 $search(x, limit)$,表示在字典树中查找与 $x$ 异或值小于 $limit$ 的数对数量。 |
| 65 | + |
| 66 | +对于 $insert(x)$ 函数,我们将数字 $x$ 按照二进制位从高到低的顺序,插入到“0-1 字典树”中,其中字典树的每个节点表示一个二进制位,每个节点有两个子节点,表示 $0$ 和 $1$。如果当前二进制位为 $0$,则插入到左子节点,否则插入到右子节点。然后将当前节点的计数值 $cnt$ 加 $1$。 |
| 67 | + |
| 68 | +对于 $search(x, limit)$ 函数,我们从字典树的根节点 `node` 开始,遍历 $x$ 的二进制位,从高到低,记当前 $x$ 的二进制位的数为 $v$。 |
| 69 | + |
| 70 | +- 如果当前 $limit$ 的二进制位为 $1$,此时我们可以直接将答案加上与 $x$ 的当前二进制位 $v$ 相同的子节点的计数值 $cnt$,然后将当前节点移动到与 $x$ 的当前二进制位 $v$ 不同的子节点,即 `node = node.children[v ^ 1]`。继续遍历下一位。 |
| 71 | +- 如果当前 $limit$ 的二进制位为 $0$,此时我们只能将当前节点移动到与 $x$ 的当前二进制位 $v$ 相同的子节点,即 `node = node.children[v]`。继续遍历下一位。 |
| 72 | + |
| 73 | +遍历完 $x$ 的二进制位后,返回答案。 |
| 74 | + |
| 75 | +有了以上两个函数,我们就可以解决本题了。 |
| 76 | + |
| 77 | +我们遍历数组 `nums`,对于每个数 $x$,我们先在字典树中查找与 $x$ 异或值小于 $high+1$ 的数对数量,然后在字典树中查找与 $x$ 异或值小于 $low$ 的数对数量,将两者的差值加到答案中。然后将 $x$ 插入到字典树中。继续遍历下一个数 $x$,直到遍历完数组 `nums`。最后返回答案即可。 |
| 78 | + |
| 79 | +时间复杂度 $O(n \times \log M)$,空间复杂度 $O(n \times \log M)$。其中 $n$ 为数组 `nums` 的长度,而 $M$ 为数组 `nums` 中的最大值。本题中我们直接取 $\log M = 16$。 |
| 80 | + |
56 | 81 | <!-- tabs:start -->
|
57 | 82 |
|
58 | 83 | ### **Python3**
|
59 | 84 |
|
60 | 85 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
61 | 86 |
|
62 | 87 | ```python
|
| 88 | +class Trie: |
| 89 | + def __init__(self): |
| 90 | + self.children = [None] * 2 |
| 91 | + self.cnt = 0 |
63 | 92 |
|
| 93 | + def insert(self, x): |
| 94 | + node = self |
| 95 | + for i in range(15, -1, -1): |
| 96 | + v = x >> i & 1 |
| 97 | + if node.children[v] is None: |
| 98 | + node.children[v] = Trie() |
| 99 | + node = node.children[v] |
| 100 | + node.cnt += 1 |
| 101 | + |
| 102 | + def search(self, x, limit): |
| 103 | + node = self |
| 104 | + ans = 0 |
| 105 | + for i in range(15, -1, -1): |
| 106 | + v = x >> i & 1 |
| 107 | + if limit >> i & 1: |
| 108 | + if node.children[v]: |
| 109 | + ans += node.children[v].cnt |
| 110 | + if node.children[v ^ 1] is None: |
| 111 | + return ans |
| 112 | + node = node.children[v ^ 1] |
| 113 | + else: |
| 114 | + if node.children[v] is None: |
| 115 | + return ans |
| 116 | + node = node.children[v] |
| 117 | + return ans |
| 118 | + |
| 119 | + |
| 120 | +class Solution: |
| 121 | + def countPairs(self, nums: List[int], low: int, high: int) -> int: |
| 122 | + ans = 0 |
| 123 | + tree = Trie() |
| 124 | + for x in nums: |
| 125 | + ans += tree.search(x, high + 1) - tree.search(x, low) |
| 126 | + tree.insert(x) |
| 127 | + return ans |
64 | 128 | ```
|
65 | 129 |
|
66 | 130 | ### **Java**
|
67 | 131 |
|
68 | 132 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
69 | 133 |
|
70 | 134 | ```java
|
| 135 | +class Trie { |
| 136 | + private Trie[] children = new Trie[2]; |
| 137 | + private int cnt; |
| 138 | + |
| 139 | + public void insert(int x) { |
| 140 | + Trie node = this; |
| 141 | + for (int i = 15; i >= 0; --i) { |
| 142 | + int v = (x >> i) & 1; |
| 143 | + if (node.children[v] == null) { |
| 144 | + node.children[v] = new Trie(); |
| 145 | + } |
| 146 | + node = node.children[v]; |
| 147 | + ++node.cnt; |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + public int search(int x, int limit) { |
| 152 | + Trie node = this; |
| 153 | + int ans = 0; |
| 154 | + for (int i = 15; i >= 0; --i) { |
| 155 | + int v = (x >> i) & 1; |
| 156 | + if (((limit >> i) & 1) == 1) { |
| 157 | + if (node.children[v] != null) { |
| 158 | + ans += node.children[v].cnt; |
| 159 | + } |
| 160 | + if (node.children[v ^ 1] == null) { |
| 161 | + return ans; |
| 162 | + } |
| 163 | + node = node.children[v ^ 1]; |
| 164 | + } else { |
| 165 | + if (node.children[v] == null) { |
| 166 | + return ans; |
| 167 | + } |
| 168 | + node = node.children[v]; |
| 169 | + } |
| 170 | + } |
| 171 | + return ans; |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +class Solution { |
| 176 | + public int countPairs(int[] nums, int low, int high) { |
| 177 | + Trie trie = new Trie(); |
| 178 | + int ans = 0; |
| 179 | + for (int x : nums) { |
| 180 | + ans += trie.search(x, high + 1) - trie.search(x, low); |
| 181 | + trie.insert(x); |
| 182 | + } |
| 183 | + return ans; |
| 184 | + } |
| 185 | +} |
| 186 | +``` |
| 187 | + |
| 188 | +### **C++** |
| 189 | + |
| 190 | +```cpp |
| 191 | +class Trie { |
| 192 | +public: |
| 193 | + Trie(): children(2), cnt(0) {} |
| 194 | + |
| 195 | + void insert(int x) { |
| 196 | + Trie* node = this; |
| 197 | + for (int i = 15; ~i; --i) { |
| 198 | + int v = x >> i & 1; |
| 199 | + if (!node->children[v]) { |
| 200 | + node->children[v] = new Trie(); |
| 201 | + } |
| 202 | + node = node->children[v]; |
| 203 | + ++node->cnt; |
| 204 | + } |
| 205 | + } |
| 206 | + |
| 207 | + int search(int x, int limit) { |
| 208 | + Trie* node = this; |
| 209 | + int ans = 0; |
| 210 | + for (int i = 15; ~i; --i) { |
| 211 | + int v = x >> i & 1; |
| 212 | + if (limit >> i & 1) { |
| 213 | + if (node->children[v]) { |
| 214 | + ans += node->children[v]->cnt; |
| 215 | + } |
| 216 | + if (!node->children[v ^ 1]) { |
| 217 | + return ans; |
| 218 | + } |
| 219 | + node = node->children[v ^ 1]; |
| 220 | + } else { |
| 221 | + if (!node->children[v]) { |
| 222 | + return ans; |
| 223 | + } |
| 224 | + node = node->children[v]; |
| 225 | + } |
| 226 | + } |
| 227 | + return ans; |
| 228 | + } |
| 229 | + |
| 230 | +private: |
| 231 | + vector<Trie*> children; |
| 232 | + int cnt; |
| 233 | +}; |
| 234 | + |
| 235 | +class Solution { |
| 236 | +public: |
| 237 | + int countPairs(vector<int>& nums, int low, int high) { |
| 238 | + Trie* tree = new Trie(); |
| 239 | + int ans = 0; |
| 240 | + for (int& x : nums) { |
| 241 | + ans += tree->search(x, high + 1) - tree->search(x, low); |
| 242 | + tree->insert(x); |
| 243 | + } |
| 244 | + return ans; |
| 245 | + } |
| 246 | +}; |
| 247 | +``` |
| 248 | +
|
| 249 | +### **Go** |
| 250 | +
|
| 251 | +```go |
| 252 | +type Trie struct { |
| 253 | + children [2]*Trie |
| 254 | + cnt int |
| 255 | +} |
| 256 | +
|
| 257 | +func newTrie() *Trie { |
| 258 | + return &Trie{} |
| 259 | +} |
| 260 | +
|
| 261 | +func (this *Trie) insert(x int) { |
| 262 | + node := this |
| 263 | + for i := 15; i >= 0; i-- { |
| 264 | + v := (x >> i) & 1 |
| 265 | + if node.children[v] == nil { |
| 266 | + node.children[v] = newTrie() |
| 267 | + } |
| 268 | + node = node.children[v] |
| 269 | + node.cnt++ |
| 270 | + } |
| 271 | +} |
| 272 | +
|
| 273 | +func (this *Trie) search(x, limit int) (ans int) { |
| 274 | + node := this |
| 275 | + for i := 15; i >= 0; i-- { |
| 276 | + v := (x >> i) & 1 |
| 277 | + if (limit >> i & 1) == 1 { |
| 278 | + if node.children[v] != nil { |
| 279 | + ans += node.children[v].cnt |
| 280 | + } |
| 281 | + if node.children[v^1] == nil { |
| 282 | + return |
| 283 | + } |
| 284 | + node = node.children[v^1] |
| 285 | + } else { |
| 286 | + if node.children[v] == nil { |
| 287 | + return |
| 288 | + } |
| 289 | + node = node.children[v] |
| 290 | + } |
| 291 | + } |
| 292 | + return |
| 293 | +} |
71 | 294 |
|
| 295 | +func countPairs(nums []int, low int, high int) (ans int) { |
| 296 | + tree := newTrie() |
| 297 | + for _, x := range nums { |
| 298 | + ans += tree.search(x, high+1) - tree.search(x, low) |
| 299 | + tree.insert(x) |
| 300 | + } |
| 301 | + return |
| 302 | +} |
72 | 303 | ```
|
73 | 304 |
|
74 | 305 | ### **...**
|
|
0 commit comments