Perl Weekly Challenge 221.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 221.
Task 1: Good Strings
Submitted by: Mohammad S Anwar
You are given a list of @words and a string $chars.
A string is good if it can be formed by characters from $chars, each character
can be used only once.
Write a script to return the sum of lengths of all good strings in words.
Example 1
Input: @words = ("cat", "bt", "hat", "tree")
$chars = "atach"
Output: 6
The good strings that can be formed are "cat" and "hat" so the answer is 3 + 3 = 6.
Example 2
Input: @words = ("hello", "world", "challenge")
$chars = "welldonehopper"
Output: 10
The strings that can be formed are "hello" and "world" so the answer is 5 + 5 = 10.
The example shows that repeated chars may be used as many times as
they are repeated. The task then is to compare the number of times
each string appears in each word to the number of times it appears in
the chars
and use the result to filter good strings. The result fits
a two-liner.
Example 1:
perl -MList::Util=all -E '
($c,@w)=@ARGV; $c{$_}++ for split "", $c; @r=grep {my %w; $w{$_}++ for split ""; all {$w{$_}<=$c{$_}}
keys %w} @w; say("chars: $c, words: @w -> good strings: @r -> length: ", length join "", @r)
' atach cat bt hat tree
Results:
chars: atach, words: cat bt hat tree -> good strings: cat hat -> length: 6
Example 2:
perl -MList::Util=all -E '
($c,@w)=@ARGV; $c{$_}++ for split "", $c; @r=grep {my %w; $w{$_}++ for split ""; all {$w{$_}<=$c{$_}}
keys %w} @w; say("chars: $c, words: @w -> good strings: @r -> length: ", length join "", @r)
' welldonehopper hello world challenge
Results:
chars: welldonehopper, words: hello world challenge -> good strings: hello world -> length: 10
The full code is:
1 # Perl weekly challenge 221
2 # Task 1: Good Strings
3 #
4 # See https://wlmb.github.io/2023/06/12/PWC221/#task-1-good-strings
5 use v5.36;
6 use List::Util qw(all);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 C W1 [W2...]
9 to find the total length of the set of words {Wn} than are formed by
10 the characters in C
11 FIN
12 my ($chars, @words)=@ARGV;
13 my %char_count;
14 $char_count{$_}++ for split "", $chars;
15 my @results=grep {
16 my %word_char_count;
17 $word_char_count{$_}++ for split "";
18 all {$word_char_count{$_}<=$char_count {$_}} keys %word_char_count
19 } @words;
20 say("chars: $chars, words: @words -> good strings: @results -> length: ",
21 length join "", @results)
Examples:
./ch-1.pl atach cat bt hat tree
./ch-1.pl welldonehopper hello world challenge
Results:
chars: atach, words: cat bt hat tree -> good strings: cat hat -> length: 6
chars: welldonehopper, words: hello world challenge -> good strings: hello world -> length: 10
Task 2: Arithmetic Subsequence
Submitted by: Mohammad S Anwar
You are given an array of integers, @ints.
Write a script to find the length of the longest Arithmetic Subsequence
in the given array.
A subsequence is an array that can be derived from another array by deleting
some or none elements without changing the order of the remaining elements.
A subsquence is arithmetic if ints[i + 1] - ints[i] are all the same value
(for 0 <= i < ints.length - 1).
Example 1:
Input: @ints = (9, 4, 7, 2, 10)
Output: 3
The longest Arithmetic Subsequence (4, 7, 10) can be derived by deleting 9 and 2.
Example 2:
Input: @ints = (3, 6, 9, 12)
Output: 4
No need to remove any elements, it is already an Arithmetic Subsequence.
Example 3:
Input: @ints = (20, 1, 15, 3, 10, 5, 8)
Output: 4
The longest Arithmetic Subsequence (20, 15, 10, 5) can be derived by deleting
1, 3 and 8.
To solve this problem I make two subroutines. One tests all arithmetic subsequences starting from the first element of a subarray and chooses the longest one. The subarrays are obtained by choping sequentially the first elements of the given array. The other subroutine searches for the second element of an arithmetic sequence given a subarray and a distance, and recurses from that element onwards. This allows a three-liner solution:
Example 1:
perl -MList::Util=max,first -E '
@s=@ARGV; say "@s -> ", max map {u(@s[$_..@s-1])} 0..@s-1; sub u(@s){return 0+@s unless @s>1;
return 1+max map {t($s[$_]-$s[0], @s[$_..@s-1])} 1..@s-1;} sub t($v, @s){$i=first
{$s[$_]==$s[0]+$v} 1..@s-1; return 1 unless defined $i; return 1+t($v, @s[$i..@s-1]);}
' 9 4 7 2 10
Results:
9 4 7 2 10 -> 3
I’ll explain with more details in the full code.
Example 2:
perl -MList::Util=max,first -E '
@s=@ARGV; say "@s -> ", max map {u(@s[$_..@s-1])} 0..@s-1; sub u(@s){return 0+@s unless @s>1;
return 1+max map {t($s[$_]-$s[0], @s[$_..@s-1])} 1..@s-1;} sub t($v, @s){$i=first
{$s[$_]==$s[0]+$v} 1..@s-1; return 1 unless defined $i; return 1+t($v, @s[$i..@s-1]);}
' 3 6 9 12
Results:
3 6 9 12 -> 4
Example 3:
perl -MList::Util=max,first -E '
@s=@ARGV; say "@s -> ", max map {u(@s[$_..@s-1])} 0..@s-1; sub u(@s){return 0+@s unless @s>1;
return 1+max map {t($s[$_]-$s[0], @s[$_..@s-1])} 1..@s-1;} sub t($v, @s){$i=first
{$s[$_]==$s[0]+$v} 1..@s-1; return 1 unless defined $i; return 1+t($v, @s[$i..@s-1]);}
' 20 1 15 3 10 5 8
Results:
20 1 15 3 10 5 8 -> 4
The full code has some more explanations:
1 # Perl weekly challenge 221
2 # Task 2: Arithmetic Subsequence
3 #
4 # See https://wlmb.github.io/2023/06/12/PWC221/#task-2-arithmetic-subsequence
5 use v5.36;
6 use List::Util qw(max first);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 N1 [N2...]
9 to find the longest arithmetic subsequence of the integers N1 N2...
10 FIN
11 my @set=@ARGV;
12 # Search longest subsequence, remove leading element and repeat. Keep max.
13 say "@set -> ", max map {test(@set[$_..@set-1])} 0..@set-1;
14
15 sub test(@subset){
16 return 0+@subset unless @subset>1; # 0 or 1 for empty or one element subsets
17 return 1+max map {rest($subset[$_]-$subset[0], @subset[$_..@subset-1])} 1..@subset-1;
18 }
19
20 sub rest($val, @subset){
21 # find index of next (the second) element of arithmetic subsequence
22 # starting at the first element $subset[0] and separated by value $val
23 my $i=first {$subset[$_]==$subset[0]+$val} 1..@subset-1;
24 return 1 unless defined $i; # none found? Only the first element is in sequence
25 return 1+rest($val, @subset[$i..@subset-1]); # recurse starting from found value
26 }
Examples:
./ch-2.pl 9 4 7 2 10
./ch-2.pl 3 6 9 12
./ch-2.pl 20 1 15 3 10 5 8
Results:
9 4 7 2 10 -> 3
3 6 9 12 -> 4
20 1 15 3 10 5 8 -> 4