【问题标题】:Unique rows after column removal from matrix从矩阵中删除列后的唯一行
【发布时间】:2014-08-30 21:37:24
【问题描述】:

给出了n 行和m 列的二进制矩阵。 nm 最多可以达到 2000 个。

我们需要知道,是否可以删除一列,这样剩余矩阵的行将是唯一的。

n = 3m = 4 的示例:

1010
0001
1001

答案是是的。我们可以删除第二列,剩下的行(110001101)将是唯一的。

n = 4m = 2 的示例:

00
01
10
11

答案是。无论我们选择哪一列,00 行都将保留。

have O(m*m*n) 蛮力算法。我通过删除每一列来检查行的唯一性。

你知道更快的算法吗?

【问题讨论】:

  • 在删除列之前,您是否假设行是唯一的?如果可能,您是否需要知道要删除哪一列?
  • @user3923424 我不想知道要删除哪一列。他们也没有这样的假设
  • 你的实现不是 O(mmn*n) 吗?对于每 m 列,您正在检查 (m-1)*n 矩阵中的行是否唯一。检查行是否获取每 n*(n-1) 对行并比较它们,testing[i]==testing[j]。但是字符串比较是 O(m),而不是 O(1)。
  • 行必须已经是唯一的,否则任务是不可能的——删除一列永远不会使两个相同的行不同。根据鸽巢原理,如果 n > 2^m 也是不可能的。

标签: algorithm language-agnostic


【解决方案1】:

!编辑:很遗憾,我的解决方案只能解决一半,抱歉。

我确定,我可以在 O(m*n) 时间内完成。

您可以在 n*m 时间内创建树。只需要一行一行地更新这个结构:

Node{
 int accessed;
 Node nextZero;
 Node nextOne;
}

如果你确实创建了这棵树,你只需要检查最后一行,如果它有“零”和“一”等于或大于两个。


有一个处理两个数字后的视觉示例。

你只是逐行,总是从根开始。

例如,当您开始处理第二行时,您从根目录开始。第二行的数字是“101”。你取第一个数字,它是“1”,所以你进入 nextOne 节点。然后你得到“0”,所以你进入 nextZero。然后你得到“1”,它不存在,所以你创建它。

毕竟你只对最深节点的“accessed”数感兴趣,如果它们都“accessed”等于1,则都是distinct,否则不存在。


伪代码

Node{
  int accessed;
  Node nextZero;
  Node nextOne;
}

bool isDistinct(){
  Node root = new Node();
  Node next;
  for (int i=0;i<arr.length;i++){
    Node actual = root;
    for (int j=0;j<arr[i].length;j++){
      if (arr[i][j] == 0){
        next = actual.nextZero;
        if (next == null){
          next = new Node();
          acutal.nextZero = next;
        }
      } else {
        next = actual.nextOne;
        if (next == null){
          next = new Node();
          acutal.nextOne = next;
        }
      }

      actual = next;
      actual.accessed++;
      if ((j == arr[i].length - 1) && (actual >= 2)){
        return false;
      }
    }
  }
  return true;
}

对不起,这实际上只是“半途而废”,我没有正确阅读我应该做什么。但经过一番思考,也许您可​​以从树中删除一个节点并有效地重新平衡它......

【讨论】:

  • 任何代码?我不喜欢你,或者请多解释一下
  • 请你用一些例子解释你的方法吗?
  • nextZeroNode 和 nextOneNode 是什么意思?
  • 不幸的是,它并不总是有效,反例:00, 01, 10, 11。不过很聪明,我喜欢。
  • @AdamStelmaszczyk - 当然可以,如果您输入 00,01,10,11,您最终会得到根节点、深度 1 的 2 个节点和深度 2 的 4 个节点。深度 2 的所有 4 个节点将有accessed 属性等于一,因此它们都是不同的。
【解决方案2】:

每一行代表一个以 10 为底的数字。

  1. 我们可以在O(n*m) 中计算所有这些数字。
  2. 我们将得到一个长度为n 的数组a
  3. 我们可以创建一个数组b,其中位置b[i] 将是我们在数组aO(n) 中拥有数字i 的次数
  4. 如果我们有一些ib[i]&gt;1,答案是否定的
  5. 不,我们将尝试逐列删除,这将相应地更改数字。例如,如果我们删除kth 列,我们需要创建数组c,它与数组b 具有相同的含义,但没有kth 列。为此,我们初始化c[i]=b[i],如果i&lt;2^k,如果b[i]=1i&gt;=2^k,那么我们将更新c[i-2^k]++。如果我们得到一些ic[i]&gt;1,答案是否定的,然后继续下一列。否则答案是肯定的。这可以在O(n*m) 中完成。

编辑:

整个解决方案的复杂度为O(n*m)

因为数字会很大,您可以使用字典将数组b 表示为稀疏数组,对于数字,您可以使用一些库来处理大数字。整个解决方案应该比蛮力更快。

【讨论】:

  • n,m 可以达到 2000,所以这些数字会非常大。您确定计算它们会达到目的吗?
  • 另外,它比我的暴力解决方案快多少?
  • n 和 m 可以达到 2000... 即使我们考虑以 2 为底的数字而不是 10,您也无法使用 long 存储此数字。无论如何,您的数组 b 和 c 将变得很大(2 ^ 2000 = 10 ^ 602)。您可以使用 BigNum 库对其进行调整以存储数字,并使用映射而不是数组,但我认为它在实践中不会快得多,因为与天真的算法相比,您的开销将是巨大的。
  • 好的,大数字可能是个问题。我认为与蛮力相比,使用此解决方案您可以更快地得到 No answer。
  • @codetest Petar 提出的解决方案是 O(nm)。 nm 个操作来创建数字,然后 m 个循环(对于每一列)进行 n 个操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 2013-07-22
  • 2021-11-14
  • 2012-09-05
  • 2021-05-17
  • 1970-01-01
相关资源
最近更新 更多