如何解决与数组中的元素的最大异或|力码
我在 Leetcode 上练习时遇到了这个问题。
问题陈述 (link):
You are given an array nums consisting of non-negative integers.
You are also given a queries array,where queries[i] = [xi,mi].
The answer to the ith query is the maximum bitwise XOR value of xi and any element of nums that does not exceed mi.
In other words,the answer is max(nums[j] XOR xi) for all j such that nums[j] <= mi.
If all elements in nums are larger than mi,then the answer is -1.
Return an integer array answer where answer.length == queries.length and answer[i] is the answer to the ith query.
约束:
1 <= nums.length,queries.length <= 10^5
queries[i].length == 2
0 <= nums[j],xi,mi <= 10^9
我使用trie方法解决了这个问题,然后去讨论部分查看其他人的解决方案。在那里,我遇到了这个解决方案 (link):
class Solution {
public:
vector<int> maximizeXor(vector<int>& nums,vector<vector<int>>& queries) {
const int n = nums.size(),q = queries.size();
vector<int> ans(q,-1);
sort(nums.begin(),nums.end());
for (int i = 0; i < q; i++) {
const int x = queries[i][0],m = queries[i][1];
if (m < nums[0]) continue;
int end = upper_bound(nums.begin(),nums.end(),m) - nums.begin();
int start = 0;
int k = 0,cur = 0;
for (int bit = 31; bit >= 0; bit--) {
if (x & (1 << bit)) { // hope A[i] this bit == 0
if (!(nums[start] & (1 << bit))) {
k |= 1 << bit;
end = lower_bound(nums.begin() + start,nums.begin() + end,cur | (1 << bit)) - nums.begin();
} else {
cur |= 1 << bit;
}
} else { // hope: A[i] this bit == 1
if (start <= end - 1 && (nums[end - 1] & (1 << bit))) {
k |= 1 << bit;
cur |= 1 << bit;
start = lower_bound(nums.begin() + start,cur) - nums.begin();
}
}
}
ans[i] = k;
}
return ans;
}
};
不幸的是,我无法理解此解决方案。如果有人能对此解决方案给出适当的解释(主要是在遍历位时),我将不胜感激。
解决方法
此实现存在一些问题。 start
和 end
应该仍然是迭代器,它们可以直接使用而无需一直添加/减去 nums.begin()
。我们谈论的是非负整数,所以只要它们适合正常的 int
第一位无论如何都是 0,所以我们应该从 int bit = 30
开始跳过一次不必要的迭代。对于整数和迭代器,start <= end - 1
比 start < end
更好。代码由一个函数组成,那么绝对不需要类,所以应该更喜欢命名空间。应用这些更改后,代码将如下所示:
namespace Solution
{
// as EXACTLY two values,std::pair is more appropriate
// we are not modifying queries,so should be accepted as const
std::vector<int> maximizeXor
(
std::vector<int>& nums,std::vector<std::pair<int,int>>const& queries
)
{
const int q = queries.size();
std::vector<int> ans(q,-1);
sort(nums.begin(),nums.end());
// remove duplicates:
// -> less numbers to iterate over
nums.erase(unique(nums.begin(),nums.end()),nums.end());
for (int i = 0; i < q; ++i)
{
int const x = queries[i].first,m = queries[i].second;
// we have a sorted array,remember?
// if first value is larger than the query maximum,then there are no
// corresponding numbers – and as the vector is initialised to -1
// anyway,the appropriate value is there already so we can simply skip
if (m < nums[0])
{
continue;
}
// using iterators pointing at the appropriate indices
auto end = upper_bound(nums.begin(),nums.end(),m);
auto start = nums.begin();
int /*k = 0,*/ cur = 0;
// intention is to check each bit of x
// modifying the loop!
//for (int bit = 30; bit >= 0; bit--)
int const MaxBit = 1 << sizeof(int) * CHAR_BIT - 2;
for (int bit = MaxBit; start != prev(end); bit >>= 1)
{
// OK; fixing an issue and adding some tricks to handle the loop
// a bit cleverer...
// sizeof(int) * CHAR_BIT: int is NOT guaranteed to have exactly
// 32 bits! if you want to be on the safe side,either calculate
// as above or use int32_t instead
// changed abort condition:
// I modified the algorithm slightly such that we can break early
// unique'ing the vector allows us to drop the original
// condition bit >= 0 entirely,this will be explained later
// I store the bit-MASK in bit now,now we do not have to
// calculate it again and again (1 << bit)
if (x & bit)
{
// so x has a 1-bit at bit index 'bit'
// in the range yet to be considered we have two groups of
// numbers:
// 1. those having a 0-bit at bit-index 'bit'
// 2. those having a 1-bit
// if we compare single bits,we get:
// x = *1***
// num = *0*** XOR: *1***
// num = *1*** XOR: *0***
// IF now there are numbers with a zero bit at all,then one
// of these will produce the maximum,whereas those with a
// 1-bit cannot asnumbers are sorted,we can just check very
// first value of the range:
// any number having a 1-bit at the same bit index will produce
// a zero-bit – thus these numbers CANNOT produce the maximum
if (!(*start & bit))
{
// bits differ,remember?
// thus the XOR will have a one-bit we store right now
// actually,we do NOT need that,we can handle that cleverer
//k |= 1 << bit;
// instead,I handle this with the NEW loop condition
// fine – there ARE numbers with zero-bits,so remove all
// numbers with 1-bit from range; as they all are at the end
// of,we simply move this one towards front:
end = lower_bound(start,end,cur | bit);
// cur contains those bits of the number producing the
// maximum that have been evaluated so far,it is a
// lower bound for – we do NOT modify it,but we can
// calculate a new upper bound from!
}
else
{
// well,there is no such number with a 0-bit
// we cannot move end or start position
cur |= bit;
}
}
else
{
// analogously:
// x = *0***
// num = *0*** XOR: *0***
// num = *1*** XOR: *1***
// so all members having a 1-bit are of interest – IF there
// are – and we can skip those numbers with 0-bit at the
// beginning
// if there are,then they are at the very end
// 'end' iterator points to one past,so we need predecessor
if (/*start < end &&*/ *prev(end) & bit)
{
// first condition is handled in the loop now
// as above: we can handle that cleverer
//k |= 1 << bit;
// now current mask NEEDS the one-bit
cur |= bit;
start = lower_bound(start,cur);
}
}
// with unchanged loop it was not possible to break early as k still
// needed to be calculated
//ans[i] = k;
// with or without early break,we can always:
ans[i] = *start ^ x;
// with every iteration,we extend the bit mask 'cur' the numbers
// have to match with by one bit (either the 0 gets confirmed
// or replaced by a 1).
// After 31 iterations (sign bit is ignored as we only have
// positive integers),*all* bits are defined (if we had omitted
// the early breaks we could have calculated
// ans[i] = cur ^ x; as well...).
// so all numbers that yet might have remained in the valid range
// must match this pattern,i. e. be equal. However as unique-ing,// there is exactly one single value left...
}
}
return ans;
}
} // namespace Solution
请注意,std::lower_bound
具有(提供随机访问迭代器,与 std::vector
一样)的复杂度为 O(log(n))
,因此执行单个查询具有 O(log(n))
和 {{ 1}} 是数字的数量。加上排序和查询 n
次的开销,与复杂度为 O(m*n) 的“原始”迭代相比,我们得到的总复杂度为 m
。如果 O(n*log(n) + m*log(n)) = O((n+m)*log(n))
与 m
具有相似的幅度或更大,则我们具有复杂性优势(已经在原始变体中,我的调整只是稍微调整常量,但不会改变复杂性)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。