【问题标题】:PHP - Find the number of groups given some constraintsPHP - 在给定一些约束的情况下查找组数
【发布时间】:2019-09-24 09:05:18
【问题描述】:

给定 n = 3 条狗和 m = 3 对敌人,a = [1, 2, 3] 和 b = [3, 3, 1],狗 1 是狗 3 的敌人,狗 3 是狗 1 和 2 的敌人。因为 3 是 1 和 2 的敌人,所以它必须在自己的容器中。狗 1 和 2 可以在一起或分开。有 4 个可能的组:{1、2}、{1}、{2}、{3}。请注意,间隔沿着从 1 到 n 连续编号的原始狗线,即在这种情况下为 [1, 2, 3]。狗不能被重新排序,狗不能被跳过,例如。 {2, 1} 和 {1, 3} 无效。

所以给出以下内容:

案例#1

n = 5
m = 2
a = (1,2)
b = (3,5)

结果是:一共可以组成11个组。

案例#2

n = 8
m = 4
a = (2,3,4,3)
b = (8,5,6,4)

结果是:一共可以组成18个组。


这是我的代码:

function countSubstrings($n, $a, $b) {
    $tokenArr = array(); 
    $x = 1;
    while ($x <= $n){
        $tokenArr[] = $x;
        $x++;
    }

    $first = 0;
    $last = $n - 1;
    $outArr   = array();
    $pointer  = 0;

    /* generate groups left to right */
    for ($i = $first; $i <= $last; $i++) {
        $outArr[$pointer][] = $tokenArr[$i];
        $tokenString = $tokenArr[$i];
        $pointer++; 
        for ($j = $i + 1; $j <= $last; $j++) {
            $tokenString .= $tokenArr[$j];
            $outArr[$pointer] = str_split($tokenString);
            $pointer++;
        }
    }

    /* find the enemeies */
    $intersects = array();
    for($k = 0; $k < count($outArr); $k++){
        if (count(array_intersect($outArr[$k], $a)) > 1 || count(array_intersect($outArr[$k], $b)) > 1) {
            $intersects[] = $outArr[$k];
        }
    }

    /* remove first and last items which are basically equal to $a and $b */
    $intersects = array_slice($intersects, 1, -1); 


    /* remove the enemeies from generated groups */
    foreach ($outArr as $keya => $valuea) {
        if (in_array($valuea, $intersects)) {
            unset($outArr[$keya]);
        }
    }

    return count($outArr);
}

到目前为止,我的代码在以下情况下有效:#1 但在 #2 上失败。

【问题讨论】:

  • 不错的尝试。相交逻辑对我来说似乎不正确,因为我们必须检查[a , b] 形成的关系,例如[1,2] 是否存在于$outArr 中。当前对count(array_intersect($outArr[$k], $a)) &gt; 1 的检查并不关心这一点。它而是检查$a 中的任何元素是否存在于$outArr[$k] 中。
  • 这不是关于array_intersect,而是关于检查的方式。无论如何,我已经添加了一个答案。
  • 我认为我们可以进一步优化这一点。完成后我会通知。

标签: php logic


【解决方案1】:

相交逻辑对我来说似乎不正确,因为我们必须检查[a , b] 形成的关系,例如[1,2] 是否存在于$outArr 中。 count(array_intersect($outArr[$k], $a)) &gt; 1 的当前检查并不关心这一点。它而是检查$outArr[$k] 中的任何元素是否存在于$a 中。

因此,将当前逻辑从:

/* find the enemeies */
    $intersects = array();
    for($k = 0; $k < count($outArr); $k++){
        if (count(array_intersect($outArr[$k], $a)) > 1 || count(array_intersect($outArr[$k], $b)) > 1) {
            $intersects[] = $outArr[$k];
        }
    }

    /* remove first and last items which are basically equal to $a and $b */
    $intersects = array_slice($intersects, 1, -1);

$intersects = array();
foreach($a as $index => $val1){
    $val2 = $b[$index];
    foreach($outArr as $current_group){
        if(in_array($val1,$current_group) && in_array($val2,$current_group)){ // check if both exist as they are enemies
            $intersects[] = $current_group;
        }
    }
}

演示: https://3v4l.org/Q2rnP

在上面的代码中,我们:

  • 在 foreach 中 $index 的帮助下,循环遍历 $a 的所有元素并同时使用 $b

  • 检查$outArr 中的当前组是否同时存在$a[$index](a.k.a $val1) 和$b[$index](a.k.a $val2) 在组中。

  • 如果两者都存在于当前组中,我们将它们置于相交之下,因为它们是敌人。你的其余逻辑是正确的。


高效解决方案:

  • 我们必须利用这一行:

一个组被定义为一个区间 (x, y),使得从 x 到 y 范围内的所有狗组成一个组。

  • 这意味着我们需要查看子数组(正如您正确判断的那样)而不是子序列。

  • 现在,我们从1 循环到N,如果我们发现一个数字左边有敌人,我们只能从那个数字+1 开始组成下一组。由于我们正在查看子数组,因此无论如何都不能包含它们之前的任何内容。

  • 例如,假设53 的敌人,在15 的一行中,并且没有其他敌人存在。因此,组队形式如下所示。

代表:

  1   2   3   4   5 
 -1  -1   5  -1   3

  |___|
  |___|___|
  |___|___|___|
      |___|
      |___|___|
          |___|


              |___| // the connection/group (4,5) remains and breaks everything before 4 since 3 is an enemy of 5 and we are looking for subarrays. So everything before 4 is disconnected anyway.
  • 所以,我们的下一个起始动物/狗是4

  • 对于每个敌人/动物,如果存在,我们会在左侧保持最近的敌人。如果存在,我们更新下一个动物来寻找上面证明的组。在下面的代码中,$prev_start 是维护下一个要查看的动物的变量。

  • 为了得到每只动物最左边的敌人,我们对敌人的详细信息进行如下预处理:

预处理:

$enemies = array_combine(range(1,$n),array_fill(0,$n,-1)); // nothing tricky, just generates an array filled with sequential numbers as keys and sets it's value as -1

   foreach($a as $index => $enemy_1){
      $enemy_2 = $b[$index]; 
      if($enemy_1 < $enemy_2){
        $enemies[$enemy_2] = max($enemies[$enemy_2],$enemy_1);
      }else if($enemy_2 < $enemy_1){
        $enemies[$enemy_1] = max($enemies[$enemy_1],$enemy_2);   
      }
   }

计算:

   $prev_start = 1;
   $count = 0; 

   for($i=1;$i<=$n;++$i){
     if($enemies[$i] !== -1){
         $prev_start = max($enemies[$i] + 1,$prev_start);
     }

     $count += ($i - $prev_start + 1);
   }
  • 由于我们预处理了敌人的详细信息,因此我们相应地更新了$prev_start,我们必须从这里重新开始计算组数。

  • $count += ($i - $prev_start + 1); 只计算要考虑计数的组(子数组)的数量。

  • 时间复杂度: O(m + n) 其中m 是对数,n 是狗/动物的数量。

  • 空间复杂度O(n) 其中n 是狗/动物的数量。

完整代码:

<?php

function countSubarrays($n, $a, $b) {
   $enemies = array_combine(range(1,$n),array_fill(0,$n,-1)); // nothing tricky, just generates an array filled with sequential numbers as keys and sets it's value as -1

   foreach($a as $index => $enemy_1){
      $enemy_2 = $b[$index]; 
      if($enemy_1 < $enemy_2){
        $enemies[$enemy_2] = max($enemies[$enemy_2],$enemy_1);
      }else if($enemy_2 < $enemy_1){
        $enemies[$enemy_1] = max($enemies[$enemy_1],$enemy_2);   
      }
   }

   $prev_start = 1;
   $count = 0; 

  for($i=1;$i<=$n;++$i){
     if($enemies[$i] !== -1){
         $prev_start = max($enemies[$i] + 1,$prev_start);
     }

     $count += ($i - $prev_start + 1);
  }

   return $count;
}

演示: https://3v4l.org/1W26C

【讨论】:

  • @Carl 我正在尝试优化一个,你的问题是m = some x pairs of enemies,所以$a(1,2,3,4,5)$b(1,3,5) 没有意义。它们在每个索引处形成一对。所以count($a)不等于count($b)是毫无疑问的。
  • @Carl 是的,请更新您的问题,最好按原样共享原始文档。
  • @Carl 所以这是一个hackerrank挑战。是的,m 对于ab 都将保持不变,但是从约束来看,蛮力是不会被接受的。
  • @Carl 查看此解决方案是否通过 3v4l.org/kh058 。一旦确认正确,我会添加解释。
  • @Carl 太好了。添加解释:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多