This is my response to The Weekly Challenge #390.
Input: $str = "2[3[a]]"
Output: "aaaaaa"
3[a] => aaa
2[3[a]] => aaa aaa
Example 2:
Input: $str = "10[a]"
Output: "aaaaaaaaaa"
Example 3:
Input: $str = "a2[b]c3[d]e"
Output: "abbcddde"
Example 4:
Input: $str = "2[a2[b]c]"
Output: "abbcabbc"
Example 5:
Input: $str = "1[a]2[b3[c]]"
Output: "abcccbccc"
#! /usr/bin/env raku
unit sub MAIN (Str $str is copy, :v(:$verbose));
say ": $str" if $verbose;
while $str ~~ / \d+ \[ /
{
$str ~~
s[ $<k> = \d+ \[ $<s> = <-[\[\]]>* \] ] = ~$<s> x $<k>.Int;
say ": $str" if $verbose;
}
say $str;
[3] Note the use if is copy so that we get a writeable
copy of the input.
See docs.raku.org/type/Parameter#method_copy for more information about is copy.
[7] As long as the string contains a digit immediately followed by an opening bracket.
[9] Find the first group whose content does not contain any brackets - i.e. an
innermost group. This regex does the job:
$<k> = \d+ \[ $<s> = <-[\[\]]>* \]$<k> is the repetition count, and $<s> is the bracket-free
content.
Then replace that group (using the substitution operator s) with
its content repeated k times, in
s[ ... ] = ~$<s> x $<k>.Int
See docs.raku.org/syntax/s/// for more information about the substitution operator s///.
[15] Print the result.
Running it:
$ ./decode-string "2[3[a]]"
aaaaaa
$ ./decode-string "10[a]"
aaaaaaaaaa
$ ./decode-string "a2[b]c3[d]e"
abbcddde
$ ./decode-string "2[a2[b]c]"
abbcabbc
$ ./decode-string "1[a]2[b3[c]]"
abcccbccc
Looking good.
With verbose mode:
$ ./decode-string -v "2[3[a]]"
: 2[3[a]]
: 2[aaa]
: aaaaaa
aaaaaa
$ ./decode-string -v "10[a]"
: 10[a]
: aaaaaaaaaa
aaaaaaaaaa
$ ./decode-string -v "a2[b]c3[d]e"
: a2[b]c3[d]e
: abbc3[d]e
: abbcddde
abbcddde
$ ./decode-string -v "2[a2[b]c]"
: 2[a2[b]c]
: 2[abbc]
: abbcabbc
abbcabbc
$ ./decode-string -v "1[a]2[b3[c]]"
: 1[a]2[b3[c]]
: a2[b3[c]]
: a2[bccc]
: abcccbccc
abcccbccc
Input: $str = "dbca", $k = 1
Output: "adbc"
Move 1: "bcad"
Move 2: "cadb"
Move 3: "adbc"
Example 2:
Input: $str = "geeks", $k = 2
Output: "eegks"
First 2 letters: "g", "e"
Move 1: "gekse" (move second letter "e")
Move 2: "gksee" (move second letter "e")
Move 3: "kseeg"
Move 4: "seegk"
Move 5: "eegks"
Example 3:
Input: $str = "cbaed", $k = 3
Output: "abcde"
First 3 letters: "c", "b", "a"
Move 1: "cbeda" (move "a")
Move 2: "cedab" (move "b")
Move 3: "edabc" (move "c")
Move 4: "eabcd" (move "d")
Move 5: "abcde" (move "e")
Example 4:
Input: $str = "fedcba", $k = 4
Output: "abcdef"
First 4 letters: "f", "e", "d", "c"
Move 1: "fdcbae" (move "e")
Move 2: "dcbaef" (move "f")
Move 3: "dcbefa" (move "a")
Move 4: "dcefab" (move "b")
Move 5: "defabc" (move "c")
Move 6: "efabcd" (move "d")
Move 7: "fabcde" (move "e")
Move 8: "abcdef" (move "f")
Example 5:
Input: $str = "perl", $k = 1
Output: "erlp"
Move 1: "erlp" (move "p")
Example 6:
Input: $str = "oloolooo", $k = 1
Output: "looloooo"
Example 7:
Input: $str = "oloooolo", $k = 1
Output: "looloooo"
This is really easy, if we disregard the instruction to actually move one character at a time until we reach the target. But let us have a go at following instructions...
$k==1, the answer is just the lowest sorted rotated version of the input
$k>1, the answer is the string with the characters reordered in sorted order
#! /usr/bin/env raku
unit sub MAIN (Str $str is copy where $str ~~ /<[a..zA..Z]>/,
Int $k where $str.chars >= $k > 0,
:a(:$all),
:v(:$verbose) = $all);
my $result;
if $k == 1
{
my @rotations = map { $str.substr($_) ~ $str.substr(0, $_) },
0 ..^ $str.chars;
say ": rotations: " ~ @rotations.raku if $verbose;
$result = @rotations.min;
while $all && $str ne $result
{
$str = $str.substr(1) ~ $str.substr(0, 1);
say ": -> $str" if $verbose;
}
}
else
{
$result = $str.comb.sort.join;
while $all && $str ne $result
{
my $index = $str.index($str.substr(0, $k).comb.min);
$str ~= $str.substr($index, 1);
$str.substr-rw($index, 1) = "";
say ": index $index -> $str" if $verbose;
}
}
say $result;
[5] Use «all mode» to get the actual moves. It enables «verbose mode» as well.
[8] The result will end up here.
[10] If we are only allowed to move the first.
[12] Get all the rotations of the input.
[17] The result is the minimum string, as min works on
strings and not just numbers.
See docs.raku.org/routine/min for more information about min.
This is where this program fails. In the second example, moving the lowest character is ok, until the last move. Then we should move the highest. But I have run out of time, so haven't figured out when to switch.
Do not use «all mode» when $k > 1, as that will lead to
an eternal loop.
[19] If you want to see the rotations, use «all mode». The loop goes on until we reach the target.
[21] Move the first character to the end.
[26] When k > 1:
[28] The result is the sorted string.
[30] As long as we have not reached the target.
[32] Get the index of the character to move. I have chosen to move the
lowest (gotten with min).
[34] Add the moved character at the end.
[36] Remove it from the original position, with substr-rw
that allows us to replace it (in this ase with an empty string).
See docs.raku.org/routine/substr-rw for more information about substr-rw.
[42] Print the result.
Running it:
$ ./order-characters-okish dbca 1
adbc
$ ./order-characters-okish geeks 2
eegks
$ ./order-characters-okish cbaed 3
abcde
$ ./order-characters-okish fedcba 4
abcdef
$ ./order-characters-okish perl 1
erlp
$ ./order-characters-okish oloolooo 1
looloooo
$ ./order-characters-okish oloooolo 1
looloooo
Looking good.
With all (and verbose) mode, but only for the ones with $k==1:
$ ./order-characters-okish -a dbca 1
: rotations: ["dbca", "bcad", "cadb", "adbc"]
: -> bcad
: -> cadb
: -> adbc
adbc
$ ./order-characters-okish -a perl 1
: rotations: ["perl", "erlp", "rlpe", "lper"]
: -> erlp
erlp
$ ./order-characters-okish -a oloolooo 1
: rotations: ["oloolooo", "looloooo", "oolooool", "oloooolo", "looooloo",
"oooolool", "oooloolo", "oolooloo"]
: -> looloooo
looloooo
$ ./order-characters-okish -a oloooolo 1
: rotations: ["oloooolo", "looooloo", "oooolool", "oooloolo", "oolooloo",
"oloolooo", "looloooo", "oolooool"]
: -> looooloo
: -> oooolool
: -> oooloolo
: -> oolooloo
: -> oloolooo
: -> looloooo
looloooo
And that's it.