I’ve been doing the Weekly
Challenges. The
latest
involved password analysis and number parsing. (Note that this ends
today.)
Task 1: Strong Password
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.
The following can be considered as one step:
- Insert one character
- Delete one character
- Replace one character with another
There are flaws with this algorithm: what of "aaaaa", which is
arguably two separate three-character repetitions, but can be fixed by
changing one character? But it seems to get the job done. In Raku:
sub strongpassword($a) {
my %ctypes;
my $reps = 0;
my $rep = 0;
my $old = '@';
Initialise the list of change counts.
my @changes;
my @chars = $a.comb;
Iterate over the characters in the password.
for @chars.kv -> $i, $c {
Look for repeats (rule 3)
if ($i > 0 && $c eq $old) {
$rep += 1;
if ($rep >= 2) {
$reps += 1;
}
} else {
$rep = 0;
$old = $c;
}
Look for existence of each specified class (rule 2)
my $t = 'n';
if ($c ~~ /<upper>/) {
$t = 'u';
} elsif ($c ~~ /<lower>/) {
$t = 'l';
} elsif ($c ~~ /<digit>/) {
$t = 'd';
}
%ctypes{$t}++;
}
Each 3-repeat is a required change.
@changes.push($reps);
Look at the number of character classes, and see how many characters
need to be added.
%ctypes{'n'}:delete;
my $k = %ctypes.keys.elems;
if ($k < 3) {
my $spare = %ctypes.values.sum - $k;
if ($spare < 0) {
@changes.push(-$spare);
}
}
Check for length.
if ($a.chars < 6) {
@changes.push(6 - $a.chars);
}
We assume that any change which fixes whatever problem there are most
of will also fix another problem, so return the maximum.
return @changes.max;
}
Task 2: Valid Number
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.
Clearly this is a job for regular expressions, so I didn't do it in
PostScript, and since Lua's equivalent lacks alternations that also
got quite fiddly. Then it's just a matter of appropriate string
escaping.
Should have done this in Rust with winnow
but it's fallen out of my
head since last year's Advent of Code.
Kotlin:
fun validnumber(a: String): Boolean {
val integer = "[-+]?[0-9]+"
val float = """[-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)"""
val exponential =
"(" + integer + "|" + float + ")[Ee]" + integer
val number =
"^(" + integer + "|" + float + "|" + exponential + ")$"
val rx = number.toRegex()
return rx.containsMatchIn(a)
}
Full code on
github.