DEV Community

Bob Lied
Bob Lied

Posted on

PWC 348 String Alike, Convert Time

PWC 348 Task 1: String Alike

The Task

You are given a string of even length. Write a script to find out whether the given string can be split into two halves of equal lengths, each with the same non-zero number of vowels.

  • Example 1:

    • Input: $str = "textbook" text | book (1, 2)
    • Output: false
  • Example 2:

    • Input: $str = "book" bo | ok (1, 1)
    • Output: true
  • Example 3:

    • Input: $str = "AbCdEfGh" AbCd | EfGh (1, 1)
    • Output: true
  • Example 4:

    • Input: $str = "rhythmmyth" rhyth | mmyth (0, 0)
    • Output: false
  • Example 5:

    • Input: $str = "UmpireeAudio" Umpire | eAudio (3, 5)
    • Output: false

Thoughts

Two steps are required: dividing the string in half, and counting vowels in each half. Let's do the obvious thing with substr to get the halves, and we can exploit the quirks of tr to count.

The examples give us a couple of important test cases: the string can contain uppercase and lowercase; and the string might not have any vowels at all. Also notice that vowels are strictly "aeiou" -- "y" is not counted as a vowel.

Code

sub strAlike($str)
{
    $str = lc($str);
    my $mid = length($str) / 2;
    my @count = map { $_ =~ tr/aeiou// }
                        substr($str, 0, $mid), substr($str, $mid);
    return $count[0] == $count[1] && $count[0] > 0;
}
Enter fullscreen mode Exit fullscreen mode

Notes:

  • lc($str) -- eliminate uppercase letters
  • $mid = ... -- assuming even length given, as specified
  • substr($str, 0, mid) -- the left half
  • substr($str, $mid) -- the right half (third argument not needed). The two substr calls result in a list of two strings, which we will pass to ...
  • @count = map { } -- count vowels in each substring and assign into an array of vowel counts
  • $_ =~ tr/aeiou// -- Ah, tr. A quirk of tr is that it returns a count of replacements made. Replace all the vowels of interest, and there's our count. Another quirk of tr is that the first argument is not a regular expression, so there are no brackets for character class here. Note the literal use of aeiou. Yet another quirk of tr is that there is no variable interpolation, so it would be awkward to try to parameterize this.
  • return ... -- The description specifies non-zero counts, so the extra condition is needed to make Example 4 work.

PWC 348 Task 2: Convert Time

The Task

You are given two strings, $source and $target, containing time in 24-hour time form. Write a script to convert the source into target by performing one of the following operations:

  1. Add 1 minute
  2. Add 5 minutes
  3. Add 15 minutes
  4. Add 60 minutes

Find the total operations needed to get to the target.

  • Example 1:
    • Input: $source = "02:30" $target = "02:45"
    • Output: 1 (Add 15 minutes)
  • Example 2:
    • Input: $source = "11:55" $target = "12:15"
    • Output: 2 (Add 15 minutes, Add 5 minutes)
  • Example 3:
    • Input: $source = "09:00" $target = "13:00"
    • Output: 4 (Add 60 minutes four times)
  • Example 4:
    • Input: $source = "23:45" $target = "00:30"
    • Output: 3 (Add 15 minutes three times)
  • Example 5:
    • Input: $source = "14:20" $target = "15:25"
    • Output: 2 (Add 60 minutes, Add 5 minutes)

Thoughts

This is a variation on the problem of making change from a set of coins, or finding the combination of stamps to pay postage.

We need to find the difference in minutes from $source to $target. Example 4 shows that we might have to cross midnight.

I could take the hours and minutes apart and do the math. But I think I'd rather use well-tested modules, so I'll use Time::Piece to parse the times and find the difference.

The operation count will be taking the biggest number of chunks possible, so in reverse order: 60, 15, 5, 1.

Code

sub convert($source, $target)
{
    use Time::Piece;

    my $s = Time::Piece->strptime($source, "%H:%M");
    my $t = Time::Piece->strptime($target, "%H:%M");
    my $min = ($t - $s)->minutes;
    if ( $min <  0 ) { $min += 24*60; }

    my $count = 0;
    for my $period ( 60, 15, 5, 1 )
    {
        $count += int($min / $period);
        $min %= $period;
    }
    return $count;
}
Enter fullscreen mode Exit fullscreen mode

Notes:

  • strptime() -- parse the time strings and create a Time::Piece object. Without any day information, this will be a time on January 1, 1970, but we don't care.
  • ($t-$s)->minutes -- Time::Piece objects can be subtracted to get a duration. That duration is actually a Time::Seconds object, which has a method that returns the duration in minutes.
  • if ( $min < 0 ) -- This covers the case where $target is after midnight. We'll add 24 hours of minutes to wrap it around back to positive.
  • The rest is math. Reduce by hours, then quarter-hours, then 5-minute blocks. Since the last block is 1 minute, this will always eventually terminate.

Top comments (0)