双向 AND 等于 0 的整数对

Integer pairs having biwise AND equal to 0

本文关键字:整数 AND 等于 双向      更新时间:2023-10-16

两个整数 x 和 y 形成一个神奇对,如果它们的按位 And 的结果等于 0。 给定一个整数数组,查找每个数组元素是否与其他数组元素形成神奇对。

输入

输入的第一行包含一个整数 T,表示测试用例的数量。 每个测试用例的第一行都有一个整数 N,表示给定数组中的元素数。 第二行包含 N 个空格分隔的整数 a1,a2,...表示给定数组的元素。

输出

对于每个测试用例,在一行中打印N个空格分隔的整数。 如果 ai 与给定数组的任何其他元素形成一个神奇的对,则 ans'i 应该等于 1。否则 ans'i 为 0。

约束

1<=N,Ai<=10^6

我尝试了蛮力。对于每个元素,我检查了这个数字的按位 AND 是否为零,数组中存在任何其他元素。显然,它的时间复杂度为 O(N^2),我的大多数测试用例都超时了

这个问题在这里:https://www.hackerearth.com/challenges/test/netapp-codenet-2017/algorithm/d2d1f6a92c6740278682e88ed42068a4/

任何人都可以建议我更好的方法或算法,以便它通过时间限制吗?

暴力破解代码:

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] << " ";

一种方法是为所有数字创建一个二叉树,就像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);
}
}

首先,很抱歉回答太长:)

问题:我认为您的蛮力问题在于您执行两次检查(在两个方向上)。此外,很多检查是不必要的。
您可以通过仅执行一次(并且仅执行必要的检查)来轻松减少迭代次数。

关键思想:您不应该从0启动内部循环。

注意:以下第一部分仅介绍第二部分,但第二部分是回答您问题的部分。

这里提供的整个代码仅用于说明所述的想法,仅此而已


1 - 找到所有可能的魔法对

在这里,我们试图找到给定向量中所有可能的魔法对,避免多次检查同一对。

解决方案可以是:

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


2 - 检查一个数字是否有神奇的对

现在我们知道了如何避免检查已经检查的对,我们将重用相同的原理来实现您想要的功能。

您要检查数组中的每个数字,它们在数组中是否有神奇的对。
在这种情况下,我们不想检查所有可能的魔法对,只有一个匹配就足以确定一个数字是否有一对。此外,当我们找到匹配项时,我们可以一次设置两个结果(对中的每个数字一个)。
您可以看到,通过这种方式,我们将能够显着减少迭代次数。

它引导我们进行如下操作:

  • 每对只检查一次
  • 在第一次匹配时停止对数字的计算
  • 确定每个匹配项的两个结果 -->如果已设置,则不执行搜索

知道了这一点,解决方案可能是:

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

, 0,

我认为您将能够非常轻松地使此示例的代码适应您的用例。


我希望它能帮助你。

您必须保存您首先执行的操作。

在示例中,您有 3 6 2 9 10

当你用蛮力做的时候,你首先要做

3 & 6

在完成所有工作之后

3 & y

你重复

6 & 3

.如果你找到避免重复这种情况的方法,你就会解决问题。