Perl Weekly Challenge 216.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 216.
Task 1: Registration Number
Submitted by: Mohammad S Anwar
You are given a list of words and a random registration number.
Write a script to find all the words in the given list that has
every letter in the given registration number.
Example 1
Input: @words = ('abc', 'abcd', 'bcd'), $reg = 'AB1 2CD'
Output: ('abcd')
The only word that matches every alphabets in the given registration
number is 'abcd'.
Example 2
Input: @words = ('job', 'james', 'bjorg'), $reg = '007 JB'
Output: ('job', 'bjorg')
Example 3
Input: @words = ('crack', 'road', 'rac'), $reg = 'C7 RA2'
Output: ('crack', 'rac')
To solve this problem I first extract the letters from the
registration. Then I build a hash for each word to test if it
contains any given letter. Finally, I check that all the letters in
the registration are contained in the word, and use that within a
grep
. The code fits a noe-and-a-half-liner:
Example 1:
perl -MList::Util=all -E '@r=grep {/[a-z]/} split "", lc shift; @a=grep {
my %l; map {$l{$_}=1} split "", lc $_; all{$l{$_}}@r} @ARGV; say "@a";
' 'AB1 2CD' abc abcd bcd
Results:
abcd
Example 2:
perl -MList::Util=all -E '@r=grep {/[a-z]/} split "", lc shift; @a=grep {
my %l; map {$l{$_}=1} split "", lc $_; all{$l{$_}}@r} @ARGV; say "@a";
' '007 JB' job james bjorg
Results:
job bjorg
Example 3:
perl -MList::Util=all -E '@r=grep {/[a-z]/} split "", lc shift; @a=grep {
my %l; map {$l{$_}=1} split "", lc $_; all{$l{$_}}@r} @ARGV; say "@a";
' 'C7 RA2' crack road rac
Results:
crack rac
The full code is similar.
1 # Perl weekly challenge 216
2 # Task 1: Registration Number
3 #
4 # See https://wlmb.github.io/2023/05/08/PWC216/#task-1-registration-number
5 use v5.36;
6 use List::Util qw(all);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 R W1 [W2...]
9 to select the words Wn that contain all the letters in the
10 registration number R.
11 FIN
12 my $reg=shift;
13 my @words=@ARGV;
14 my @letters_reg=grep {/[a-z]/} split "", lc $reg;
15 my @result=grep
16 {
17 my %letters;
18 map {$letters{$_}=1} split "", lc $_;
19 all {$letters{$_}} @letters_reg
20 } @words;
21 say "Registration number: $reg, words: @words, output: @result";
Example:
./ch-1.pl 'AB1 2CD' abc abcd bcd
./ch-1.pl '007 JB' job james bjorg
./ch-1.pl 'C7 RA2' crack road rac
Results:
Registration number: AB1 2CD, words: abc abcd bcd, output: abcd
Registration number: 007 JB, words: job james bjorg, output: job bjorg
Registration number: C7 RA2, words: crack road rac, output: crack rac
#+endsrc
Task 2: Word Stickers
Submitted by: Mohammad S Anwar
You are given a list of word stickers and a target word.
Write a script to find out how many word stickers is needed to make up
the given target word.
Example 1:
Input: @stickers = ('perl','raku','python'), $word = 'peon'
Output: 2
We just need 2 stickers i.e. 'perl' and 'python'.
'pe' from 'perl' and
'on' from 'python' to get the target word.
Example 2:
Input: @stickers = ('love','hate','angry'), $word = 'goat'
Output: 3
We need 3 stickers i.e. 'angry', 'love' and 'hate'.
'g' from 'angry'
'o' from 'love' and
'at' from 'hate' to get the target word.
Example 3:
Input: @stickers = ('come','nation','delta'), $word = 'accommodation'
Output: 4
We just need 2 stickers of 'come' and one each of 'nation' & 'delta'.
'a' from 'delta'
'ccommo' from 2 stickers 'come'
'd' from the same sticker 'delta' and
'ation' from 'nation' to get the target word.
Example 4:
Input: @stickers = ('come','country','delta'), $word = 'accommodation'
Output: 0
as there's no "i" in the inputs.
To solve this task, I first map each letter to the stickers which may
provide them. Then I call a recursive subroutine to find all possible
solutions and choose the best. This subroutine has as inputs the
currently available letters (none at the beginning), from stickers that have been already
used, and the letters we still have to find from the given word. I
provide as many letters as possible from the available letters.
If they are insufficient, I choose in turn each of the stickers that
provide a missing letter and recurse. The result is the number of
stickers that are used or undef
if there is no solution. From all the possible choices, I
choose the best. The code fits an incomprehensible three liner and a
better written full code below.
Example 1:
perl -MList::Util=min -E '
($w,@s)=@ARGV; for $s(@s){push @{$s{$_}}, $s for split "", lc $s}; say 0+r({},[split "", lc $w]);
sub r($a,$l){@l=@$l;%a=%$a;my @r=grep{$c=$a{$_};$c&&$a{$_}--;!$c}@l;@r||return 0;my @p;for(@{$s{$r[0]}})
{ %g=%a;$g{$_}++ for split "", lc $_;push @p, r(\%g,\@r)}$m=min grep{defined} @p;return 1+$m if defined $m;undef}
' peon perl raku python
Results:
2
Example 2:
perl -MList::Util=min -E '
($w,@s)=@ARGV; for $s(@s){push @{$s{$_}}, $s for split "", lc $s}; say 0+r({},[split "", lc $w]);
sub r($a,$l){@l=@$l;%a=%$a;my @r=grep{$c=$a{$_};$c&&$a{$_}--;!$c}@l;@r||return 0;my @p;for(@{$s{$r[0]}})
{ %g=%a;$g{$_}++ for split "", lc $_;push @p, r(\%g,\@r)}$m=min grep{defined} @p;return 1+$m if defined $m;undef}
' goat love hate angry
Results:
3
Example 3:
perl -MList::Util=min -E '
($w,@s)=@ARGV; for $s(@s){push @{$s{$_}}, $s for split "", lc $s}; say 0+r({},[split "", lc $w]);
sub r($a,$l){@l=@$l;%a=%$a;my @r=grep{$c=$a{$_};$c&&$a{$_}--;!$c}@l;@r||return 0;my @p;for(@{$s{$r[0]}})
{ %g=%a;$g{$_}++ for split "", lc $_;push @p, r(\%g,\@r)}$m=min grep{defined} @p;return 1+$m if defined $m;undef}
' accommodation come nation delta
Results:
4
Example 4:
perl -MList::Util=min -E '
($w,@s)=@ARGV; for $s(@s){push @{$s{$_}}, $s for split "", lc $s}; say 0+r({},[split "", lc $w]);
sub r($a,$l){@l=@$l;%a=%$a;my @r=grep{$c=$a{$_};$c&&$a{$_}--;!$c}@l;@r||return 0;my @p;for(@{$s{$r[0]}})
{ %g=%a;$g{$_}++ for split "", lc $_;push @p, r(\%g,\@r)}$m=min grep{defined} @p;return 1+$m if defined $m;undef}
' accommodation come country delta
Results:
0
Below is the commented full code:
1 # Perl weekly challenge 216
2 # Task 2: Word Stickers
3 #
4 # See https://wlmb.github.io/2023/05/08/PWC216/#task-2-word-stickers
5 use v5.36;
6 use List::Util qw(min);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 W S1 [S2...]
9 to find how many stickers S1 S2... are required to make the word W
10 FIN
11 my ($word, @stickers)=@ARGV;
12 # Map letters to stickers
13 my %stickers_with_letter;
14 for my $s(@stickers){
15 push @{$stickers_with_letter{$_}}, $s for split "", lc $s;
16 }
17 # At first there are no available letters and I have to provide all letters in word
18 my $result=0+solve({}, [split "", lc $word]);
19 # Print result
20 say "Word: $word, stickers: @stickers, result: $result";
21
22 sub solve($available, $letters){
23 my @letters=@$letters;
24 my %available=%$available;
25 # Use as many available letters as possible to provide required letters
26 my @remaining=grep {my $a=$available{$_}; $available{$_}-- if $a; !$a} @letters;
27 # Done?
28 return 0 unless @remaining;
29 my $first=$remaining[0];
30 my @possible_results;
31 # Iterate over all possible stickers that provide the first remaining letter
32 for(@{$stickers_with_letter{$first}}){
33 # Add sticker's letters to available set
34 my %augmented=%available;
35 $augmented{$_}++ for split "", lc $_;
36 # solve recursively for the remaining letters
37 push @possible_results, solve(\%augmented, \@remaining);
38 }
39 # Choose best solution
40 my $best=min grep {defined} @possible_results;
41 # Add the sticker used and return, if there is solution
42 return 1+$best if defined $min;
43 return undef;
44 }
45
Examples:
./ch-2.pl peon perl raku python
./ch-2.pl goat love hate angry
./ch-2.pl accommodation come nation delta
./ch-2.pl accommodation come country delta
Results:
Word: peon, stickers: perl raku python, result: 2
Word: goat, stickers: love hate angry, result: 3
Word: accommodation, stickers: come nation delta, result: 4
Word: accommodation, stickers: come country delta, result: 0