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 character6-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
/;