PWC199 - Good Triplets

TL;DR

On with TASK #2 from The Weekly Challenge #199. Enjoy!

The challenge

You are given an array of integers, @array and three integers $x,$y,$z.

Write a script to find out total Good Triplets in the given array.

A triplet array[i], array[j], array[k] is good if it satisfies the following conditions:

a) 0 <= i < j < k <= n (size of given array)
b) abs(array[i] - array[j]) <= x
c) abs(array[j] - array[k]) <= y
d) abs(array[i] - array[k]) <= z

Example 1

Input: @array = (3,0,1,1,9,7) and $x = 7, $y = 2, $z = 3
Output: 4

Good Triplets are as below:
(3,0,1) where (i=0, j=1, k=2)
(3,0,1) where (i=0, j=1, k=3)
(3,1,1) where (i=0, j=2, k=3)
(0,1,1) where (i=1, j=2, k=3)

Example 2

Input: @array = (1,1,2,2,3) and $x = 0, $y = 0, $z = 1
Output: 0

The questions

Just to nit-pick a bit, I’d argue that condition a) is technically correct although it might be misleading as it is a little too broad. It allows one excess element from the array (both indexes 0 and n seem to be included in the range).

I’ll stop at k = n - 1 anyway.

The solution

I’m getting too old for this s…tuff.

I came out with the obvious $O(N^3)$ algorithm, but I know that there must be something better.

Anyway.

Perl goes first this time:

#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';

my ($x, $y, $z, @list) = @ARGV ? @ARGV : (7, 2, 3, 3, 0, 1, 1, 9, 7);
say good_triplets($x, $y, $z, @list);

sub good_triplets ($x, $y, $z, @list) {
   my $count = 0;
   for my $i (0 .. $#list - 2) {
      for my $j ($i + 1 .. $#list - 1) {
         next if abs($list[$i] - $list[$j]) > $x;
         for my $k ($j + 1 .. $#list) {
            next if abs($list[$j] - $list[$k]) > $y
                 || abs($list[$i] - $list[$k]) > $z;
            ++$count;
         }
      }
   }
   return $count;
}

Then… Raku:

#!/usr/bin/env raku
use v6;
sub MAIN ($x, $y, $z, *@list) { put good-triplets($x, $y, $z, @list) }

sub good-triplets ($x, $y, $z, *@list) {
   return [+] gather for 0 .. (@list - 3) -> \i {
      for (i + 1) .. (@list - 2) -> \j {
         next if (@list[i] - @list[j]).abs > $x;
         for (j + 1) ..^ @list -> \k {
            next if (@list[j] - @list[k]).abs > $y
                 || (@list[i] - @list[k]).abs > $z;
            take 1;
         }
      }
   };
}

Yes, it leaves a bitter taste. Let’s get some candy and move on.

Cheers!


Comments? Octodon, Twitter, GitHub, Reddit, or drop me a line!