Perl Weekly Challenge 312

Minimum Time and Balls and Boxes

Published: 10 Mar 2025

Task 1: Minimum Time

You are given a typewriter with lowercase english letters a to z arranged in a circle.

Typing a character takes 1 sec. You can move the pointer one character clockwise or anti-clockwise.

The pointer initially points at a.

Write a script to return the minimum time it takes to print the given string.

Example 1

Input: $str = "abc" Output: 5 The pointer is at 'a' initially. 1 sec - type the letter 'a' 1 sec - move pointer clockwise to 'b' 1 sec - type the letter 'b' 1 sec - move pointer clockwise to 'c' 1 sec - type the letter 'c'

Example 2

Input: $str = "bza" Output: 7 The pointer is at 'a' initially. 1 sec - move pointer clockwise to 'b' 1 sec - type the letter 'b' 1 sec - move pointer anti-clockwise to 'a' 1 sec - move pointer anti-clockwise to 'z' 1 sec - type the letter 'z' 1 sec - move pointer clockwise to 'a' 1 sec - type the letter 'a'

Example 3

Input: $str = "zjpc" Output: 34

Logic

To find the minimum time to type the string on a circular typewriter:

  1. Start at position 0 (letter 'a').
  2. For each character, calculate the clockwise and anti-clockwise distances from the current position.
  3. Take the minimum of the two distances (since the letters are arranged in a circle, going the other way around might be shorter).
  4. Add the move time plus 1 second for typing the character.
  5. Update the current position and continue to the next character.

The key insight is that on a circle of 26 letters, the distance between two positions is min(|a-b|, 26 - |a-b|).

Perl Solution

ch-1.pl

#!/usr/bin/env perl use v5.38; use warnings; use feature 'signatures'; no warnings 'experimental::signatures'; ## no critic (Subroutines::ProhibitSubroutinePrototypes) use Type::Params qw(compile); use Types::Standard qw(Str); sub _pos ($ch) { return ord($ch) - ord('a') } sub minimum_time ($str) { ($str) = compile(Str)->($str); my $time = 0; my $pos = 0; # 'a' for my $ch ( split //, $str ) { my $target = _pos($ch); my $diff = abs( $target - $pos ); my $move = $diff < ( 26 - $diff ) ? $diff : ( 26 - $diff ); $time += $move + 1; # move + type $pos = $target; } return $time; }

Python Solution

ch-1.py

#!/usr/bin/env python3 """Minimum Time - Perl Weekly Challenge 312 task 1.""" from __future__ import annotations from collections.abc import Sequence import sys import unittest def minimum_time(text: str) -> int: """ Return minimum time to type text on a circular typewriter (a-z). Start at 'a'. Each move step costs 1 second; typing each character costs 1 second. """ pos = 0 total = 0 for ch in text: target = ord(ch) - ord("a") diff = abs(target - pos) total += min(diff, 26 - diff) + 1 pos = target return total class MinimumTimeExamples(unittest.TestCase): """Example-based tests from the specification.""" def test_example_1(self) -> None: self.assertEqual(minimum_time("abc"), 5) def test_example_2(self) -> None: self.assertEqual(minimum_time("bza"), 7) def test_example_3(self) -> None: self.assertEqual(minimum_time("zjpc"), 34)

Task 2: Balls and Boxes

There are n balls of mixed colors: red, blue or green. They are all distributed in 10 boxes labelled 0-9.

You are given a string describing the location of balls.

Write a script to find the number of boxes containing all three colors. Return 0 if none found.

Example 1

Input: $str = "G0B1R2R0B0" Output: 1 The given string describes there are 5 balls as below: Box 0: Green(G0), Red(R0), Blue(B0) => 3 balls Box 1: Blue(B1) => 1 ball Box 2: Red(R2) => 1 ball

Example 2

Input: $str = "G1R3R6B3G6B1B6R1G3" Output: 3 The given string describes there are 9 balls as below: Box 1: Red(R1), Blue(B1), Green(G1) => 3 balls Box 3: Red(R3), Blue(B3), Green(G3) => 3 balls Box 6: Red(R6), Blue(B6), Green(G6) => 3 balls

Example 3

Input: $str = "B3B2G1B3" Output: 0 Box 1: Green(G1) => 1 ball Box 2: Blue(B2) => 1 ball Box 3: Blue(B3) => 2 balls

Logic

To find the number of boxes containing all three colors:

  1. Parse the input string as pairs of (color, box_digit).
  2. For each pair, add the color to the corresponding box's set of colors.
  3. After processing all pairs, count how many boxes have all three colors (R, G, B) in their set.
  4. Return the count (0 if none).

The input string has even length, with each pair consisting of a color letter (R/G/B) followed by a box digit (0-9).

Perl Solution

ch-2.pl

#!/usr/bin/env perl use v5.38; use warnings; use feature 'signatures'; no warnings 'experimental::signatures'; ## no critic (Subroutines::ProhibitSubroutinePrototypes) use Type::Params qw(compile); use Types::Standard qw(Str); sub balls_and_boxes ($str) { ($str) = compile(Str)->($str); die 'Expected an even-length string of (Color,Box) pairs' if length($str) % 2 != 0; my @boxes; for my $i ( 0 .. 9 ) { $boxes[$i] = {}; } my @chars = split //, $str; for ( my $i = 0; $i < @chars; $i += 2 ) { my $color = $chars[$i]; my $box = $chars[ $i + 1 ]; die 'Invalid color' if $color !~ /\A[RGB]\z/; die 'Invalid box' if $box !~ /\A[0-9]\z/; $boxes[$box]{$color} = 1; } my $count = 0; for my $box ( 0 .. 9 ) { ++$count if $boxes[$box]{R} && $boxes[$box]{G} && $boxes[$box]{B}; } return $count; }

Python Solution

ch-2.py

#!/usr/bin/env python3 """Balls and Boxes - Perl Weekly Challenge 312 task 2.""" from __future__ import annotations from collections.abc import Sequence import sys import unittest def balls_and_boxes(text: str) -> int: """Return number of boxes containing all three colors (R/G/B).""" if len(text) % 2 != 0: raise ValueError("Expected an even-length string of (Color,Box) pairs") boxes: list[set[str]] = [set() for _ in range(10)] for idx in range(0, len(text), 2): color = text[idx] box = text[idx + 1] if color not in {"R", "G", "B"}: raise ValueError("Invalid color") if box not in "0123456789": raise ValueError("Invalid box") boxes[int(box)].add(color) return sum(1 for colors in boxes if {"R", "G", "B"}.issubset(colors)) class BallsAndBoxesExamples(unittest.TestCase): """Example-based tests from the specification.""" def test_example_1(self) -> None: self.assertEqual(balls_and_boxes("G0B1R2R0B0"), 1) def test_example_2(self) -> None: self.assertEqual(balls_and_boxes("G1R3R6B3G6B1B6R1G3"), 3) def test_example_3(self) -> None: self.assertEqual(balls_and_boxes("B3B2G1B3"), 0)