Perl Weekly Challenge 287.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 287.

Task 1: Strong Password

Submitted by: Mohammad Sajid Anwar
You are given a string, $str.

Write a program to return the minimum number of steps required to make the given string
very strong password. If it is already strong then return 0.

Criteria:

- It must have at least 6 characters.
- It must contains at least one lowercase letter, at least one upper case letter
  and at least one digit.
- It shouldn't contain 3 repeating characters in a row.
Following can be considered as one step:

- Insert one character
- Delete one character
- Replace one character with another
Example 1
Input: $str = "a"
Output: 5
Example 2
Input: $str = "aB2"
Output: 3
Example 3
Input: $str = "PaaSW0rd"
Output: 0
Example 4
Input: $str = "Paaasw0rd"
Output: 1
Example 5
Input: $str = "aaaaa"
Output: 3

A seemingly simple solution would be:

  • If the password is missing characters of one class (lowercase, uppercase, digit) add one character.
  • If it has a consecutive sequence of 3 equal characters, replace the last one.
  • If the resulting string has n<6 characters, and a character 6-n times.

If there is a sequence of 3 equal characters and a character class is missing I can replace one of the repeated characters by one of the missing class and solve both problems with just one step. On the other hand, if there is a sequence of 3 equal characters and the string is too short, I could break the sequence by adding, instead of replacing, a character, solving two problems. The added character could also be of a missing class, if any, thereby solving three problems. However, for example, for a string of 5 consecutive characters a replacement would solve the problem of repeated characters, while adding a new character still leave a problematic string of 3 consecutive characters. For example, …aaaaa… could become …aabaa… after a replacement, or …aabaaa… after an addition, producing a longer string if needed, but still with three consecutive a’s. In both cases, b could be replaced by B or 0 if characters of another class are required. This could lead to a complicated solution for passwords with a large minimum length. Nevertheless, as the required length is small, 6, it turns out that the only problematic case would be that of a string made up of exactly 3 equal characters, for which addition is better than substitution. For example, aaa can be converted to aaAa and then to aaAa0 and to aaAa0a, whixh is better than converting aaa to aAa, aAa0, aAa0a and aAa0aa. As I don’t have to actually convert the password, just to count the required number of steps, this leads to a code that fits into a two liner.

Examples:

perl -MList::Util=max -E '
for(@ARGV){$s=/^(.)\1\1$/;$c=(!/[a-z]/)+(!/[A-Z]/)+(!/[0-9]/);my $t;++$t while/(.)\1\1/g;
$c=max($c-$t,0);$h=max(6-(length)-$c,0);say "$_ -> ", $s?3:$t+$h+$c;}
' a aB2 PaaSW0rd Paaasw0rd aaaaa aaa

Results:

a -> 5
aB2 -> 3
PaaSW0rd -> 0
Paaasw0rd -> 1
aaaaa -> 2
aaa -> 3

The next to last solution differs from that in the statement of the problem, but I can verify it is correct by constructing the strong password step by step:

step  password  action
0 aaaaa  
1 aaAaa replace by upper case
2 aaAaa0 add digit

Similarly, I construct the solution for the last example:

step  password  action
0 aaa  
1 aaAa add upper case char
2 aaAa0 add digit
3 aaAa0x add any other char

The full code is:

 1  # Perl weekly challenge 287
 2  # Task 1:  Strong Password
 3  #
 4  # See https://wlmb.github.io/2024/09/16/PWC287/#task-1-strong-password
 5  use v5.36;
 6  use List::Util qw(max);
 7  die <<~"FIN" unless @ARGV;
 8      Usage: $0 P1 P2...
 9      to find the minimum number of steps required to convert
10      the passwords Pi into strong passwords
11      FIN
12  for(@ARGV){
13      my $single_triad=/^(.)\1\1$/;
14      my $missing_classes=(!/[a-z]/)+(!/[A-Z]/)+(!/[0-9]/);
15      my $triads=0;
16      ++$triads while /(.)\1\1/g;
17      my $missing_classes=max($missing_classes-$triads, 0);
18      my $missing_chars=max(6-(length)-$missing_classes, 0);
19      my $steps=$single_triad?3:$triads+$missing_chars+$missing_classes;
20      say "$_ -> $steps";
21  }

Examples:

./ch-1.pl a aB2 PaaSW0rd Paaasw0rd aaaaa aaa

Results:

a -> 5
aB2 -> 3
PaaSW0rd -> 0
Paaasw0rd -> 1
aaaaa -> 2
aaa -> 3

Task 2: Valid Number

Submitted by: Mohammad Sajid Anwar
You are given a string, $str.

Write a script to find if it is a valid number.

Conditions for a valid number:

- An integer number followed by an optional exponent.
- A decimal number followed by an optional exponent.
- An integer number is defined with an optional sign
  '-' or '+' followed by digits.

Decimal Number:

A decimal number is defined with an optional sign '-'
or '+' followed by one of the following definitions:
- Digits followed by a dot '.'.
- Digits followed by a dot '.' followed by digits.
- A dot '.' followed by digits.

Exponent:

An exponent is defined with an exponent notation 'e' or 'E'
followed by an integer number.

Example 1
Input: $str = "1"
Output: true
Example 2
Input: $str = "a"
Output: false
Example 3
Input: $str = "."
Output: false
Example 4
Input: $str = "1.2e4.2"
Output: false
Example 5
Input: $str = "-1."
Output: true
Example 6
Input: $str = "+1E-8"
Output: true
Example 7
Input: $str = ".44"
Output: true

I build and use a regular expression to capture the rules above. I explain the expression in the full code below. The code fits a one-liner.

Examples:

perl -E '
say "$_ -> ", 0+/^(\+|-)?(\d+|\d+\.\d*|\d*\.\d+)((e|E)(\+|-)?\d+)?$/?"true":"false" for @ARGV;
' 1 a . 1.2e4.2 -1. +1E-8 .44

Results:

1 -> true
a -> false
. -> false
1.2e4.2 -> false
-1. -> true
+1E-8 -> true
.44 -> true

In the full code I use an extended regular expression to make it more intelligible.

 1  # Perl weekly challenge 287
 2  # Task 2:  Valid Number
 3  #
 4  # See https://wlmb.github.io/2024/09/16/PWC287/#task-2-valid-number
 5  use v5.36;
 6  die <<~"FIN" unless @ARGV;
 7      Usage: $0 S1 S2...
 8      to find which strings Si represent valid numbers.
 9      FIN
10  my $re = qr/      # regular expression
11     ^                # start of string
12     (\+|-)?          # optional sign
13     (\d+             # integer
14      | \d+\.\d*      # or integer part and optional decimal part
15      | \d*\.\d+      # or optional integer part and decimal part
16     )
17     (                # exponent
18      (e|E)           #   letter e or E
19      (\+|-)?         #   optional sign
20      \d+             #   integer exponent
21     )?               # is optional
22     $                # end of string
23     /x;            # extended syntax
24  say "$_ -> ", /$re/?"true":"false" for (@ARGV);

Examples:

./ch-2.pl 0 1 a . 1.2e4.2 -1. +1E-8 .44

Results:

0 -> true
1 -> true
a -> false
. -> false
1.2e4.2 -> false
-1. -> true
+1E-8 -> true
.44 -> true

/;

Written on September 16, 2024