Shortest 11 Editorial
And once again, we come to an end to yet another shortest solution competition.It was a very close call until the very end, including a lot of turn arounds as well as a lot of plot twists and not a clear winner until the very end.
You can see all of the solutions here, as well as an editorial of the best and most creative solutions to follow. Please note that if there are multiple solutions that are exact copies of each other, a solution will be chosen by the time it had been submitted as the winning one (as in, the first time it has been submitted). Here are some general reminders of things used in shortest solution contests to avoid repetition in the editorial:
Whenever possible to squish the solution into one line, writing
name = lambda __self__, param1, param2: "insert code here"
is shorter than writing
def name(__self__, param1, param2):
return "insert code here"
def name(__self__, param1, param2):
is the same as writing
def name(_, a, b):
(the _ for
__self__
can be anything else)
100 Transform
Short description: Get from one array to another by either decrementing/decrementing an element or shifting the array to the right.
The winning solution is: (anikov)
class Transform:
minTransformations = lambda _,a,b: min(i + sum(abs(j - k) for j,k in zip(a,b[i:] + b)) for i in range(51))
The solution breakdown is as follows:
- The solution revolves around simulating all of the possible combinations of changes to the array we might trigger so that we get the desired result
- This includes the sum of absolute differences between the corresponding elements in the lists plus the number of shifts the array had received previously
-
min(list)
is the way of getting a minimum in a list in Python (keep in mind that the list provided to the method does not have to consist of integers)
-
zip(a, b)
is a way to get a list of tuples of the like of
(a[i], b[i])
for each i from 0 to the length of either of the lists, assuming the lists are of same length -
if a and b do not have the same length however,
zip(a, b)
will provide a list of(a[i], b[i])
tuples for each in in the natural number range of[0, min(len(a), len(b)) ]
- Instead of shifting list a around, having
zip(a[-k:] + a[:-k], b)
zip(a, b[k:] + b[:k])
or while taking the previous point into account we would get
zip(a, b[k:] + b) - It is enough to do
range(51)
to go through every possible combination of operations because the length of the array is at most 50 (meaning anything above it would just cycle through combinations over and over) - Doing something along the lines of:
for i, j in list_of_tuples
110 GreaterOrEqual
Short description: Given an array of integers, find a the smallest possible positive integer X, for which exactly K elements from the array are greater than or equal to it - or return -1 if there no such X exists
The winning solution is: (Lepluto)
class GreaterOrEqual:
def getMinimumX(_, l, k):
b, *s = sorted(l + [0])[~k:]
return -(b in s or ~b)
The solution breakdown is as follows:
- It's very important to note that instead of brute forcing the solution we can find one doing a few very simple operations. Instead of running through all of the elements for every possible integer within the constraints (which will still pass the time constraints), we can simply sort the array and look for an element for which we know the condition is satisfied implicitly. Given a sorted version of the array called sorted_list, this would leave us with three options:
- If the
(k - 1)th
number from the back ofsorted_array
(let us call it S) is not equal to thekth
one (let us call it T), then the answer isS + 1
- If S is equal to T on the other hand, the answer is always
-1
- If K equals zero, the answer is
sorted_array[sorted_array.length() - 1] + 1
K == 0
we simply handle an edge case of trying to find an integer that is out of the range of the array. - If the
- Writing
~k
is the same as writing-k - 1
in Python sorted(l)
provides us with an inline sorted copy of the list- The trick covered by writing
sorted(l + [0])
is to add one more element to the list, or in this case the number 0. We add 0 because the constraints strictly specify we will have positive integers only in the array. Adding another seemingly arbitrary integer to the list takes care of ourK == 0
problem entirely since now the list will not go out of bounds - Writing something along the lines of
head, *tail = some_arbitrary_list
is the same as writing
head = some_arbitrary_list[0]
tail = some_arbitrary_list[1:]
in Python. It is called unpacking and it is a very neat trick used in code golfing. some_arbitrary_list[-k:]
is the same as writingsome_arbitrary_list [len(some_arbitrary_list) - k:]
, or essentially giving us a range of elements fromlen(some_arbitrary_list) - k
tolen(some_arbitrary_list) - 1
inclusive- Instead of checking if
b == s[0]
, we can simply check ifb in s
- and this will be the same due to the sorted nature of the list (if b exists in s, then it is exclusively equal tos[0]
, since otherwise the sort would be broken) - In the event that the condition
b in s
does not go through, calculating the negative of~b
is preciselyb + 1
, whereas if it is true then the answer would be -1
200 BeautifulString
Short description: Find the minimal distance between two identical characters in a given string
The winning solution is: (martinkozle)
class BeautifulString:
remove = lambda s, t: min(min(map(len, (' %s ' % t).split(i))) + 1 for i in t) % 99 - 1
The solution breakdown is as follows:
- The winning solution uses a simple concept combined with a very clever manipulation of empty spaces not being counted in code golfing to avoid using conditions whenever an answer is not available. The approach is a simple brute force one, splitting the modified string by every character and calculating the residual length of characters that are left after the split.
map(function, list)
creates amapped_list
of elements, such that each elementmapped_list[i] == function(list[i])
- The solution revolves around putting exactly 98 spaces on each side of the string, so that when it is split by a particular character we get
98 + len(substring)
, or 98 characters at a minimum (likely more) to which we later on add 1 - This way, calculating the result modulo 99 later on does two things:
- If a solution exists,
result % 99 - 1
will cut off any residual length in the string (since we specifically added 98 spaces to each side of the string) and provide us with the correct solution - If a solution does not exist, it is guaranteed that the minimum would be exactly 99 (since the string would be split on the first character during the first iteration and the residue would be exactly 98 + 1), providing us with
99 % 99 - 1 == -1
, which is the correct solution to the problem
- If a solution exists,
Although this is an extremely cool and unconventional way of solving this problem, I would like to point out two other solutions who had the same amount of points but entertained a bit of a different approach:
- The solution by anikov:
class BeautifulString:
remove = lambda _,a: min(a[i:].find(a[i],1) % 99 for i in range(len(a))) % 98 - 1 - The solution by haris:
class BeautifulString:
def remove(_, s):
m = -1
for c in s:
s = s[1:]
f = s.find(c)
if f % 99 < m % 99:
m = f
return m - Both of the solutions rely on modular arithmetics to normalize the result returned by the find method in Python - the nearest index if one exists or -1 if it does not
- Essentially, normalizing it like this would guarantee that the result of finding the minimum would be in the integer range of [0, 98], meaning using modulo 98 afterwards would allow us to find -1 without wasting characters on unnecessary conditionals
- The caveat in haris' solution is that he normalizes the values themselves while checking instead of normalizing and recalculating the result, which ends up being the same amount of characters
210 WorldOfWarcraft
Short description: Find the smallest sum of consecutive numbers from an array (starting at the beginning) that is larger than a number X
The winning solution is: (Lepluto)
class WorldOfWarcraft:
def getExperience(_,r, n, d):
for i in d:
r -= i
if r < 0:
return -r
The solution breakdown is as follows:
- This was a very easy problem to golf. The solution is a complete brute force simulation of the requirements and is short enough as it is.
- There are no particular tricks used in this solution, apart from the fact that n is never used anywhere because there is no need
300 WordPuzzle
Find the length of the most reoccurring substring consisting of consecutive dots ('.') across all rows of the input list of strings
The winning solution is: (martinkozle)
class WordPuzzle:
getMostReoccuringLength = lambda s, b: -max(range(-99, 0), key=[i.count('.') / ~('.X.' in i) for i in b].count)
The solution breakdown is as follows:
- The solution itself does not use any particular properties to solve the problem in a faster way, it's just a normal bruteforce done in a very, very clever way
max(list, key = function)
is a way of getting the logical maximum of a list based on a certain function to calculate the key under which we get the maximum- Instead of counting each length of the consecutive character strings, we can go through each possible length (so basically a range from 0 to 99) and check how many times each possible length appears. The way
max()
works in Python is that if there are multiple values that appear that satisfy the key function, it takes the first one that appears. With this said, instead of going from 0 to 99 we can go from 99 to 0 and make sure that the maximum value would be acknowledged every time. - The more intuitive way to get this range would be to go through it with
range(99, 0, -1)
, however the optimal way for this particular problem would be to run something similar to-range(-99, 0)
which will become evident a bit later in the explanation max(range(x), key = list.count)
is the same as writingmax([list.count(i) for i in range(x)])
- The cool part of this solution relies completely on the fact that the test cases happened to be very weak - in particular for all test cases but 1, each string row has 'X' characters only at the beginning or at the end of the string - meaning we can completely abuse this fact to gain more characters
- If this had been true for all of the test cases, it is obvious that a simple count of '.' of each row would grant us a correct solution. Fortunately, the only case that this solution does not work for provides double the length of the actual solution, so dividing the answer by
~('.X.' in i)
gives us two cases:
- If
'X'
does exist in a certain line, we will divide by-1 -1 = -2
, which provides us with the correct solution for the only test case that can't be abused - If
'X'
does not exist in a certain line, we will divide by-1
, meaning we get the correct length for that row
- If
- Since the solution is obviously negative now due to the division, we can use the range through the negative symmetric values of the range so that the count function works (it will search for negative values
- Finally, since the max function would also return a negative value now (from
range(-99, 0)
), to get the correct solution we just need to write a minus sign before the entire snippet and it will be done
Keep in mind that there are countless counter examples to this solution - however since it works for these test cases in particular it's completely valid and very creative.
Following is also a solution (that is 2 characters longer) but would work for any test case:
class WordPuzzle:
getMostReoccuringLength = lambda _, b: max(('X'.join(b).split('X').count("." * i), i) for i in range(1, 99))[1]
The short explanation for this one would be:
string.join(list_of_strings)
is basically a concatenation of every element inlist_of_strings
withstring
between each concatenation'X'.join(b).split('X')
simply converts the entire list of strings to a list of strings containing only consecutive '.' characters- We then count every value of
'.' * i
for every i inrange(1, 99)
to go through all of the possible strings that could appear inside and create tuples of the format(length_count, length)
for every possible value - Running a
max()
function over a list of tuples finds the maximum by the first value of the tuple - and if multiple maxima exist it finds the maximum between them by the second tuple value as a break rule - We then pick the second value of the maximum tuple as this is the answer to our problem
To sum everything up, very big congratulations to martinkozle for the extraordinary and proficient solutions, as well as to everyone else involved and dedicated to this huge community event. It was a really close call until the very end and fun and interesting for everyone trying to escape their everyday and monotonous problems.
With that said, we really hope we can organize a lot more of these types of competitions in the future.