site.base_url }}/">The Bear's Den -->

The Bear's Den

Enter at your own risk

Good Keys

Task 1: Good Integer

Submitted by: Mohammad Sajid Anwar


You are given a positive integer, $int, having 3 or more digits.

Write a script to return the Good Integer in the given integer or -1 if none found.

A good integer is exactly three consecutive matching digits.

Example 1

Input: $int = 12344456
Output: "444"

Example 2

Input: $int = 1233334
Output: -1

Example 3

Input: $int = 10020003
Output: "000"

Solution

This task has some similarity to task 1 from week 280.

We are looking for a character that is followed by itself two times and that is neither preceded by itself nor follows the two repetitions.

Again, we need a (negative) lookbehind assertion containing a reference to a capture group. Such reference is regarded as having an unknown variable length - despite the fact that the capture group has a fixed length of one. While in week 280 this restriction could be circumvented by reversing the string and searching from the end (as seen in the solutions by alexander-karelas and deadmarshal), this doesn’t seem to work for this task: we need to look ahead and behind.

But another trick from week 280 can be used. After capturing a single character, we can make a negative lookbehind assertion consisting of a lookahead assertion for the captured character followed by two arbitrary characters. Applied at the position after the captured character, it will match the character preceding it and it has a fixed length of two.

Obviously, this concept is not limited to integers.

use strict;
use warnings;

sub good_integer {
    (shift =~ /((.)(?<!(?=\2)..)\2{2})(?!\2)/)[0];
}

See the full solution to task 1.

Task 2: Changing Keys

Submitted by: Mohammad Sajid Anwar


You are given an alphabetic string, $str, as typed by user.

Write a script to find the number of times user had to change the key to type the given string. Changing key is defined as using a key different from the last used key. The shift and caps lock keys won’t be counted.

Example 1

Input: $str = 'pPeERrLl'
Ouput: 3

p -> P : 0 key change
P -> e : 1 key change
e -> E : 0 key change
E -> R : 1 key change
R -> r : 0 key change
r -> L : 1 key change
L -> l : 0 key change

Example 2

Input: $str = 'rRr'
Ouput: 0

Example 3

Input: $str = 'GoO'
Ouput: 1

Solution

We perform a search for a single character that is followed by itself any number of times ignoring case. A global match in list context produces the individual keys. Put into scalar context, it gives the number of keys, which is one more than the number of key changes.

At first glance, it seems we were done at this point. But there is a subtlety with case-insensitive matching: characters are compared case-folded and some letters have a variant with the same folded case as their lowercase/uppercase counterparts but do not reside on the same key. Examples are ς (Greek Small Letter Final Sigma) and ß (Latin Small Letter Sharp S) on a Greek resp. German keyboard. All of the following strings would erroneously be identified as “single key”:

/^(.)\1$/i && say "$_ matches" for qw(ßss ßSS σς Σς);

So actually we must not match case-insensitive, but instead need to lowercase the string. This is in contrast to the rules given in fc’s documentation.

use strict;
use warnings;

sub changing_keys {
    (() = lc(shift) =~ /(.)\1*/gi) - 1;
}

See the full solution to task 2.


If you have a question about this post or if you like to comment on it, feel free to open an issue in my github repository.