The Weekly Challenge - 265


TASK #2: Completing Word
You are given a string, $str containing alphnumeric characters and array of strings (alphabetic characters only), @str.

Write a script to find the shortest completing word. If none found return empty string.

A completing word is a word that contains all the letters in the given string, ignoring space and number. If a letter appeared more than once in the given string then it must appear the same number or more in the word.


Here my solution again with CPAN module Statistics::Frequency:

#!/usr/bin/perl
use strict;
use warnings;
use Statistics::Frequency;

=begin
In this task, you need to answer two questions:
1. Does each letter of the given string ($given_str) occur in an element ($element) of the word array (@str)?
2. Is the frequency of a letter in the given string ($given_str) Less Than or Equal To the frequency in an element ($element) of the word array (@str)?

To put it another way:

if( (index($element, $char_given_str) == -1) || $f1->frequency($char_given_str) > $f2->frequency($char_given_str)) -> no success

where $f1 refers to $given_str and $f2 to an element of @str
=cut

sub completing_word {

  my($given_str, @str) = @_;

  my @result = ();
  
  # get characters [a-z] from the given string
  my @given_str_chars = grep( /[a-z]/, (split //, lc($given_str)) );
  # create a new Statistics::Frequency object to invoke later the 'frequency' method
  my $f1 = Statistics::Frequency->new(@given_str_chars);

  # extract all unique characters from $given_str to avoid duplicate results
  my $unique_chars_given_str = join ("", @given_str_chars);
  $unique_chars_given_str =~ tr/a-z//s;

  # evaluate each element in word array @str by an INNER loop
  OUTER: foreach my $element (@str) {  
	
       # create a new Statistics::Frequency object to invoke later the 'frequency' method
       my $f2 = Statistics::Frequency->new( (split //, $element) );

       my $success = 0;
       INNER: foreach my $char ((split //, $unique_chars_given_str)) {

           if( (index($element, $char) == -1) || $f1->frequency($char) > $f2->frequency($char)) {
             $success = 0;
             # terminate loop INNER and investigate next element (if exists)                        
             last INNER;  		 
           }	 
           $success++;
       }
       push(@result, $element) if($success);   
  }

  # if there is more than one string that satisfies the condition
  if(scalar @result > 1) {
      my @result_sorted = sort( { length($a) <=> length($b) } @result );
      # print the smallest array element	 
      print("Result: $result_sorted[0]\n");
  }
  else {
      print("Result: @result\n");	
  }

}

# TESTS

my($given_str, @str);

# Example 1
$given_str = 'aBc 11c';
@str = ('accbbb', 'abc', 'abbc');
completing_word($given_str, @str); # accbbb

# Example 2
$given_str = 'Da2 abc';
@str = ('abcm', 'baacd', 'abaadc');
completing_word($given_str, @str); # baacd

# Example 3
$given_str = 'JB 007';
@str = ('jj', 'bb', 'bjb');
completing_word($given_str, @str); # bjb 

# Example 4
$given_str = 'JB 007';
@str = ('jz', 'bb', 'bjb');
completing_word($given_str, @str); # bjb