Puzzles
Below are various puzzles and games.
Number Puzzles
Primality Test
Given a positive integer write an algorithm that returns true if is prime, and false otherwise.
A naive solution is to iterate up to This would get the job done, but it runs on time. We can bring that down to time by relying on the fact that the largest divisor of an integer (say some integer ), will always be In other words, we only need to check for divisors up to
primality-test
- Input: An integer
- Output: if otherwise.
- if return
- else
- init
- while do
- if return
- else
- goto
- return
Note that we use the square of the divisor rather than Square roots are fairly nasty objects — both in real analysis and on computers — causing problems on the comparison operators because of floating point arithmetic.
Array Puzzles
Rain Trapping
Given non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.
function f(height:number[]) {
const L = height.length;
let left = 0;
let right = L-1;
let leftmax = height[left];
let rightmax = height[right];
let result = 0;
while (left < right) {
if (leftmax < rightmax) {
left++;
leftmax = leftmax > height[left] ? leftmax : height[left];
result += leftmax-height[left]
} else {
right--;
rightmax = rightmax > height[right] ? rightmax : height[right];
result += rightmax - height[right];
}
}
return result;
}
String Puzzles
For the string puzzles, unless otherwise stated, characters are mapped to ASCII.
Decompress
Given a string of the form where is an integer and is a string, write an algorithm that returns the decompressed form of the string — the string repeated times. For example, the string results in The string results in We assume the strings are well-formed.
The algorithm below is one possible approach. The idea is to have one pointer behind another pointer We use to move through the string If the character is a numeral (represented by the function below), we we increment Otherwise, we enter an innerloop that concatenates the string from to
decompress
- Input: A string
- Output: An uncompressed string
- while
- if then
- else
- while
- return
For example, suppose the string is Initially, both and are on the first character:
This is a numeral, so moves forward:
In doing so, is no longer on a numeral, so we take the slice from to That's the numeral so we cast it to an integer, then loop up to that cast. At each iteration, we concatenate the the character is currently pointing to. At the end of that loop, we increment and update to
At the next iteration, we have on another numeral:
The previous process repeats:
resulting in the state:
Now, when encounters another numeral followed by another numeral, we simply keep incrementing:
Leaving us with:
In the worst case, this algorithm runs on where is the length of the string, and is the length of an expansion. The time complexity may be worse, however, depending on how the and (concatenation) functions are implemented. In a language like JavaScript, those operations run on These bottlenecks can be avoided by pushing the characters to the array, then joining all of the array elements after the loops. This problem is not encountered in languages like C++.
Compress
Given a string write an algorithm that replaces 2 or more consecutive terms of the same character into the number of occurrences followed by the character. For example,
This implementation uses the same approach as the uncompress problem. Note the use of the do-while loop. Unlike the uncompress algorithm, this approach takes There's an inner-while loop, but that loop is purely there to make the code more concise. The pointer increments so long as and point to terms of the same character.
compress
- Input: A string
- Output: A compressed string,
- while
- do
- while
- if then
- do
- return
Anagrams
Given two strings and write an algorithm that returns true (or 1) if and are anagrams, and false (or 0) otherwise. Anagrams are strings that contain the same characters, disregarding order.
an incorrect, tempting solution would be to iterate over the first string, accumulating the sum of each element's character code. Then iterate over the second string, subtracting each element's character code from the sum. If two strings have the same set of characters (an anagram), the result should be 0, so we return 1, otherwise we return 0. Before any of these operations occur, we immediately return if the two strings do not have the same length (strings of unequal lengths are not anagrams by definition):
anagram
- Input: a string and a string
- Output: Boolean.
- if return
- init
- if
start loop body
- goto
end loop body
- return
The temptation here is strong: We have a linear time complexity and constant memory complexity. Except there's a case it doesn't cover: and If the characters were encoded via ASCII, we would have and This yields in which case we return true. Clearly, however, the two strings are not anagrams.
A correct approach would be to use a hash table:
anagram
- Input: a string and a string
- Output: Boolean.
- if return
- init
- if
start loop 1 body
- if then
- goto
end loop 1 body
- if
start loop 2 body
- if then return
- else
- goto
end loop 2 body
- for each do
start loop 3 body
- if return
- return
end loop 3 body
Here, we have a linear time complexity, where is the length of either string, and is the number of entries in the hash table. This comes at the cost of memory complexity. The time complexity also depends on the hash table implementation.
Most Frequent Character
Given a string write an algorithm that returns the most frequent character of the string. If there are ties, return the earliest character.
A hash table would work here.
most frequent character
- Input: a string
- Output: a string the most frequent character.
- init
- if then
begin loop body
- if then
- else
- if then
- goto
end loop body
- init
- for each do
- if then
- if then
- return
This algorithm has a running time of where is the length of the string and is the number of entries in the hash table. Given that we have Like other hash table solutions, a complete time complexity analysis requires an examining the hash table's implementation. The memory complexity here is where is the size of the hash table.
Roman Numeral Conversion
Write an algorithm that converts a given Roman numeral into its Hindu-arabic equivalent.
romnum
Let be a finite, non-empty string of length over the alphabet Further, let be a mapping from defined as
For each character with , the function returns the sum
- while do
-
- return