如果两个整数x和y的按位与结果等于0,则它们构成一个神奇的对。 给定一个整数数组,请为每个数组元素查找它是否与其他数组元素形成了魔幻对。
输入
输入的第一行包含一个整数T,该整数表示测试用例的数量。 每个测试用例的第一行都有一个整数N,表示给定数组中元素的数量。 第二行包含N个单个以空格分隔的整数a1,a2,...,它表示给定数组的元素。
输出
对于每个测试用例,在一行中打印N个空格分隔的整数。 如果ai与给定数组的任何其他元素形成一个魔幻对,则ans'i应该等于1。否则ans'i为0。
约束
1 <= N,Ai <= 10 ^ 6
我尝试过蛮力。对于每个元素,我检查此数字的按位与是否为零,或者是否与数组中存在的任何其他元素相同。显然,它的时间复杂度为O(N ^ 2),我的大多数测试用例都超时了
有人能建议我一种更好的方法或算法,使其超过时间限制吗?
暴力代码:
int n;
cin >> n;
int a[n];
for (int i = 0; i < n; i++)
cin >> a[i];
int ans[n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (a[i] & a[j] == 0)
ans[i] = 1;
for (int i = 0; i < n; i++)
cout << ans[i] << " ";
答案 0 :(得分:5)
一种方法是为所有数字创建一个二叉树,如Trie。
例如,如果您有数组3 6 2 9 10,则二进制数组看起来像
arr = 11,110,10,1001,1010,树想要
root
/ \
0 1
\ / \
1 0 1
/ \ /
0 1 0
\ \
1 1
如果我们遍历二进制数组中的每个元素,则与条件匹配的数字(对于元素中的每个设置位应为0,对于元素中的未设置位应为0或1)。
现在,我们只需要将这些位遍历到树中即可。如果我们能够做到,那么至少存在一个满足条件的数字。
时间复杂度O(N)。 原因:-有n个数字。每个数字均为32位二进制长度。创建新节点将使用O(1)。因此,O(32N)=> O(N)。 inv_arr的时间相同。
注意:尝试将数字转换为32位二进制数字,因为它将覆盖范围内指定的所有数字。否则会导致问题。这里的6和9构成了一个魔幻对,但是inv_arr中的0110不能被遍历,并且由于无法完成最左边0的遍历,因此将导致不存在任何魔幻对。如果所有数字都用相同长度的二进制表示,则遍历树将给出正确的答案。
代码
public class BinaryNode {
char c;
public BinaryNode left;
public BinaryNode right;
public BinaryNode(char c) {
this.c = c;
}
}
public class BinaryTree {
public BinaryNode root;
public BinaryTree(char c) {
root = new BinaryNode(c);
}
public void addToTree(String s) {
BinaryNode node = this.root;
int length = s.length();
for (int i = s.length()-1; i >= 0; i--) {
BinaryNode newNode;
if (s.charAt(i) == '0') {
newNode = addCharToTree(node.left, s.charAt(i));
node.left = newNode;
} else {
newNode = addCharToTree(node.right, s.charAt(i));
node.right = newNode;
}
node = newNode;
}
}
private BinaryNode addCharToTree(BinaryNode node, char c) {
if (node == null)
return new BinaryNode(c);
return node;
}
}
public class Solution {
private static void findMagicalPairs(List<Integer> list) {
// for creating 32 char long binary string list
List<String> binaryNumberList = list.stream()
.map(num -> Long.toBinaryString( Integer.toUnsignedLong(num) | 0x100000000L ).substring(1))
.collect(Collectors.toList());
// dummy character as root
BinaryTree binaryTree = new BinaryTree('c');
binaryNumberList.forEach(binaryTree::addToTree);
List<Boolean> booleanList = binaryNumberList.stream()
.map(s -> hasMagicalPair(s, binaryTree.root))
.collect(Collectors.toList());
}
private static boolean hasMagicalPair(String s, BinaryNode node) {
if (s == null || s.length() == 0)
return true;
if (node == null)
return false;
String substring = s.substring(0, s.length() - 1);
if (s.charAt(s.length()-1) == '1')
return hasMagicalPair(substring, node.left) ;
return hasMagicalPair(substring, node.left) || hasMagicalPair(substring, node.right);
}
}
答案 1 :(得分:1)
首先,很长的回答很抱歉:)
问题::我认为您的强力问题在于您每次执行两次检查(双向检查)。而且,很多检查都是不必要的。
通过只进行一次检查(并且只进行必要的检查),可以轻松地减少迭代次数。
主要思想:您不应从0
开始内循环。
注意::以下第一部分仅介绍第二部分,而第二部分是回答您问题的部分。
此处提供的整个代码仅用于说明陈述的想法,仅此而已。
。在这里,我们试图在给定向量中找到所有可能的魔术对,以避免多次检查同一对。
解决方案可能是:
std::vector<std::pair<int, int>> magical_pairs(const std::vector<int> & data)
{
std::vector<std::pair<int, int>> result;
for(size_t i = 0; i < data.size()-1; ++i) // Stop at second to last
{
for(size_t j = i+1; j < data.size(); ++j) // Start from i+1 and not 0
{
if((data[i] & data[j]) == 0)
result.push_back(std::make_pair(data[i], data[j]));
}
}
return result;
}
这样,您只需检查一次所有可能的对。
根据我的说法,如果您想获得所有可能的魔法对,您所降低的复杂性不会比仅检查一次所有可能的对降低。
但是如果有人拥有更好的解决方案,我将非常有兴趣听到(阅读)。
您可以通过以下方式运行示例:
std::vector<int> input_array {3, 12, -6, 27, 8, 18, -66, 47, 11}; // input example
for(const std::pair<int, int> & mp : magical_pairs(input_array))
std::cout << mp.first << " : " << mp.second << std::endl;
此示例的结果:
3:12
3:8
12:18
8:18
现在,我们知道如何避免检查已检查的对,我们将重用相同的原理来实现所需的功能。
您要检查数组中的每个数字在数组中是否都具有魔术对。
在这种情况下,我们不想检查所有可能的魔术对,只有一个匹配就足够了确定一个数字是否有一对。此外,当找到匹配项时,我们可以一次设置两个结果(该对中的每个数字一个)。
您可以看到,通过这种方式,我们将能够显着减少迭代次数。< / p>
它导致我们继续进行以下操作:
知道这一点,解决方案可能是:
std::vector<bool> has_magical_pair(const std::vector<int> & data)
{
std::vector<bool> result(data.size(), false);
for(size_t i = 0; i < data.size()-1; ++i) // From 0 to second to last
{
if(!result[i]) // search for a magical pair only if not already found
{
for(size_t j = i+1; j < data.size(); ++j) // From i+1 to last
{
if((data[i] & data[j]) == 0)
{
// Set two results at a time
result[i] = true;
result[j] = true;
break; // Exit the inner loop at first match
}
}
}
}
return result;
}
这样,您将比蛮力方法更有效 。
您可以通过以下方式运行示例:
std::vector<int> input_array {3, 12, -6, 27, 8, 18, -66, 47, 11};
for(bool hmp : has_magical_pair(input_array))
std::cout << hmp << ", ";
std::cout << std::endl;
此示例的结果:
1,1,0,0,1,1,0,0,0,
我认为您将能够很轻松地使此示例的代码适应您的用例。
希望它能对您有所帮助。
答案 2 :(得分:-1)
您必须保存您首先执行的操作。
在该示例中,您有3 6 2 9 10
当你用蛮力去做的时候,你首先要做
3&6
在完成所有
之后3&y
您重复
6&3
。如果您找到避免重复此操作的方法,则将解决问题。