Question Name: Rectangle-Builder-Greater-Area or RectangleBuilderGreaterArea

This key is the binary search.

def solution(A, X): fence_count = {} for fence in A: fence_count[fence] = fence_count.get(fence, 0) + 1 num_of_pens = 0 usable_fences = [] for fence in fence_count: if fence_count[fence] < 2: # Less than one pair. We cannot use it. continue elif fence_count[fence] < 4: usable_fences.append(fence) else: usable_fences.append(fence) # We consider the square pen here. if fence * fence >= X: num_of_pens += 1 # We consider the non-square pen here. usable_fences.sort() candidate_size = len(usable_fences) for i in xrange(candidate_size): # Use binary search to find the first fence pair, that # could be used with current pair to form a pen. begin = i + 1 end = candidate_size - 1 while begin <= end: mid = (begin + end) // 2 if usable_fences[mid] * usable_fences[i] >= X: end = mid - 1 else: begin = mid + 1 # Now the usable_fences[end + 1] is the first qualified # fence. combination_num = candidate_size - (end + 1) num_of_pens += combination_num if num_of_pens > 1000000000: return -1 return num_of_pens]]>

Question Name: Dwarfs-Rafting or DwarfsRafting

Need some simple mathematical knowledge. The code is unnecessarily long. A 2-d array is better to write much shorter code. However, I keep the long version for better readability.

def solution(N, S, T): quadrant_left_front = (N // 2) * (N // 2) quadrant_left_back = (N // 2) * (N // 2) quadrant_right_front = (N // 2) * (N // 2) quadrant_right_back = (N // 2) * (N // 2) boundary = N // 2 # Compute how many slots are available in each quadrant. for barrel in S.split(): # Adjust to 0-based index. row = int(barrel[:-1]) - 1 column = ord(barrel[-1]) - ord("A") if row < boundary: # The barrel is in the front. if column < boundary: # The barrel is in the left. quadrant_left_front -= 1 else: # The barrel is in the right. quadrant_right_front -= 1 else: # The barrel is in the back. if column < boundary: # The barrel is in the left. quadrant_left_back -= 1 else: # The barrel is in the right. quadrant_right_back -= 1 # lf is short for left front, etc. # To keep balance, we need: # 1. weight_lf + weight_lb = weight_rf + weight_rb # 2. weight_lf + weight_rf = weight_rf + weight_rb # Solve the equations and we can get the answer: # 1. weight_lf = weight_rb # 2. And weight_rf = weight_lb allowance_lf = min(quadrant_left_front, quadrant_right_back) allowance_rb = min(quadrant_left_front, quadrant_right_back) allowance_lb = min(quadrant_left_back, quadrant_right_front) allowance_rf = min(quadrant_left_back, quadrant_right_front) # Minus the seats, which are already occupied by dwarfs. for dwarf in T.split(): # Adjust to 0-based index. row = int(dwarf[:-1]) - 1 column = ord(dwarf[-1]) - ord("A") if row < boundary: # The dwarf is in the front. if column < boundary: # The dwarf is in the left. allowance_lf -= 1 if allowance_lf < 0: return -1 else: # The dwarf is in the right. allowance_rf -= 1 if allowance_rf < 0: return -1 else: # The dwarf is in the back. if column < boundary: # The dwarf is in the left. allowance_lb -= 1 if allowance_lb < 0: return -1 else: # The dwarf is in the right. allowance_rb -= 1 if allowance_rb < 0: return -1 return allowance_lf + allowance_rb + allowance_lb + allowance_rf]]>

Question Name: Slalom-Skiing or SlalomSkiing

This is a wonderful variant of longest increasing subsequence. Because the longest increasing subsequence is a very classic question withe O(NlogN) solution, the key point to solve this question is to covert its original form to the classic question. We would like to extend it with two mirror universe.

+--------+--------+--------+ | | | | | | | | |Original| mirror | mirror | | | #1 | #2 | | Points | | | | | | | | | | | | | | | +--------+--------+--------+

The original question is convert into: find the longest increasing subsequence in the new extended multiverse. Here is an example of extended multiverse:

Original universe: +------+ |X | | X | | X | | X | | X | +------+ Extended multiverse: +------+------+------+ |X | X|X | | X | X | X | | X | X | X | | X | X | X | | X | X | X | +------+------+------+

For each point P, it has three instances: the instance in the original world as P0, one in the mirror world as P1, and the last in the double-mirror world as P2. We process P2 firstly, then P1, and finally P0, so that to avoid the self-link sequence. In other words, we do not want that P2 is connected from P0 or P1, because they are essentially the same point.

The relationship between the original qualified subsequence (change direction at most two times) and the new longest increasing subsequence is 1:N mapping. Actually every subsequence in the original question has multiple equivalent subsequence in the new multiverse. Oppositely, every subsequence in the new multiverse has one equivalent in the original world.

def LongestIncreasingSubsequence(seq): ''' The classic dynamic programming solution for longest increasing subsequence. More details could be found: https://en.wikipedia.org/wiki/Longest_increasing_subsequence http://www.geeksforgeeks.org/dynamic-programming-set-3-longest-increasing-subsequence/ http://stackoverflow.com/questions/3992697/longest-increasing-subsequence ''' # smallest_end_value[i] = j means, for all i-length increasing # subsequence, the minmum value of their last elements is j. smallest_end_value = [None] * (len(seq) + 1) # The first element (with index 0) is a filler and never used. smallest_end_value[0] = -1 # The length of the longest increasing subsequence. lic_length = 0 for i in range(len(seq)): # Binary search: we want the index j such that: # smallest_end_value[j-1] < seq[i] # AND # ( smallest_end_value[j] > seq[i] # OR # smallest_end_value[j] == None # ) # Here, the result "lower" is the index j. lower = 0 upper = lic_length while lower <= upper: mid = (upper + lower) // 2 if seq[i] < smallest_end_value[mid]: upper = mid - 1 elif seq[i] > smallest_end_value[mid]: lower = mid + 1 else: raise "Should never happen: " + \ "the elements of A are all distinct" if smallest_end_value[lower] == None: smallest_end_value[lower] = seq[i] lic_length += 1 else: smallest_end_value[lower] = \ min(smallest_end_value[lower], seq[i]) return lic_length def solution(A): # We are solving this question by creating two mirrors. bound = max(A) + 1 multiverse = [] for point in A: # The point in the double-mirror universe. multiverse.append(bound * 2 + point) # The point in the mirror universe. multiverse.append(bound * 2 - point) # The point in the original universe. multiverse.append(point) return LongestIncreasingSubsequence(multiverse)]]>

Question Name: Flood-Depth or FloodDepth

This is a variant of Trapping Rain Water by LeetCode. The undergoing concept is the same. However, the implementation is a bit different.

from collections import namedtuple def solution(A): if len(A) < 3: return 0 # The blocks in the stack is in strictly height descending order. # For the first block in the stack, its max_depth is maximum water # depth of its (exclusive) left area. # The other blocks' max_depth is the maximum water depth between its # previous block in the stack and itself, both exclusive. Block = namedtuple("Block", ["height", "max_depth"]) stack = [Block(A[0],0)] for height in A[1:]: if height == stack[-1].height: # These two adjacent blocks have the same height. They act # totally the same in building any water container. continue elif height < stack[-1].height: stack.append(Block(height, 0)) else: max_depth = 0 # Let the current iterating block be C, the previous two # blocks in the stack be A and B. And their positions are # demoed as: # C # A C # A ... B ... C # while the blocks between A and B are omitted. So do the # blocks between B and C. # # The additional_depth consider the blocks A, B, and C only, # and igonres all the omitted blocks, such as: # C # A C # A B C (no block is between A and B, or B and C) # # HOWEVER, the additional_depth is not always the maximum # water depth between A and C, because there may be some # water between A and B, or B and C, as exists in the omitted # blocks. We need to adjust the additional_depth to get the # maximum water depth between A and C, both exclusive. while len(stack) > 1 and height > stack[-1].height: additional_depth = min(stack[-2].height, height) - \ stack[-1].height max_depth = max(max_depth, stack[-1].max_depth) + \ additional_depth stack.pop() # Combine leftward same-or-less-height blocks. These dropped # blocks are never going to be part of the remaining water # container. while len(stack) > 0 and height >= stack[-1].height: max_depth = max(max_depth, stack[-1].max_depth) stack.pop() stack.append(Block(height, max_depth)) overall_max_depth = 0 for block in stack: if block.max_depth > overall_max_depth: overall_max_depth = block.max_depth return overall_max_depth]]>

Question Name: Longest-Password or LongestPassword

The solution could be more Pythonic with some additional space.

def solution(S): longest = -1 num_of_letters = 0 num_of_digits = 0 num_of_others = 0 for letter in S: if letter.isalpha(): num_of_letters += 1 elif letter.isdigit(): num_of_digits += 1 elif letter == " ": # Check whether it's a valid password. if num_of_others == 0 and \ num_of_letters % 2 == 0 and \ num_of_digits % 2 == 1: if longest < num_of_letters + num_of_digits: longest = num_of_letters + num_of_digits # Reset the counters for the next word. num_of_letters = 0 num_of_digits = 0 num_of_others = 0 else: num_of_others += 1 # Check whether the last word is a valid password. if num_of_others == 0 and \ num_of_letters % 2 == 0 and \ num_of_digits % 2 == 1: if longest < num_of_letters + num_of_digits: longest = num_of_letters + num_of_digits return longest]]>

Question Name: Sql-Sum or SqlSum

This question is too easy to be a serious training lesson. Most likely, it’s a live test for SQL challenges on Codility.com. The following solution works in both PostgreSQL and SQLite.

SELECT SUM(v) FROM elements]]>

Question Name: Contains Duplicate

In general, I found two solutions. The first solution is using hash set:

class Solution(object): def containsDuplicate(self, nums): """ :type nums: List[int] :rtype: bool """ deduplicated = set(nums) return len(deduplicated) != len(nums)

The second solution is using sorting. We could sort the input and check each pair of adjacent items. Or we could early terminate the sorting if we found two same numbers.

class Solution_I(object): def containsDuplicate(self, nums): """ :type nums: List[int] :rtype: bool """ nums.sort() for index in xrange(len(nums) - 1): if nums[index] == nums[index + 1]: return True return False ################################################ def compare(num1, num2): if num1 < num2: return -1 elif num1 > num2: return 1 else: raise Exception("Duplicate is found.") return 0 class Solution_II(object): def containsDuplicate(self, nums): """ :type nums: List[int] :rtype: bool """ try: nums.sort(cmp = compare) except Exception: return True else: return False]]>

Question Name: Combination Sum III

This is a typical recursion problem. Find out the base case and recursive cases, and that’s the right solution.

class Solution(object): def _combinationSum3Helper(self, remaining, candidates, next_value, partial_sum, target): """ :type remaining: int. It's the count of additional numbers could be used. :type candidates: list[bool]. candidates[i] is True, if the number i is\ used. :type next_value: int. It's the next number to consider. :type partial_sum: int. The sum of used candidates so far. :type target : int. It's the target sum. :rtype: List[List[int]] """ result = [] if remaining == 0: if partial_sum == target: # Found one combination. combination = [] for i in xrange(1, next_value): if candidates[i] == True: combination.append(i) result.append(combination) return result else: # This combination is not valid. return result elif partial_sum + next_value > target or next_value > 9: # It's impossbile to have any valid combination with current # prefix. return result else: # It does not take the next_value. result.extend(self._combinationSum3Helper( remaining, candidates, next_value + 1, partial_sum, target)) # It does take the next_value. candidates[next_value] = True result.extend(self._combinationSum3Helper( remaining - 1, candidates, next_value + 1, partial_sum + next_value, target)) candidates[next_value] = False return result def combinationSum3(self, k, n): """ :type k: int :type n: int :rtype: List[List[int]] """ candidates = [False for _ in xrange(10)] # 0 is never used. return self._combinationSum3Helper(k, candidates, 1, 0, n)]]>

Question Name: Kth Largest Element in an Array

This is exactly the quick-select.

class Solution(object): def _quickSortHelper(self, nums, begin, end): """ :type nums: List[int] :type begin: int :type end: int :rtype: int (the index of nums[begin] after quick-select) """ next_smaller_pos = begin + 1 next_bigger_pos = end while next_smaller_pos <= next_bigger_pos: if nums[next_smaller_pos] <= nums[begin]: next_smaller_pos += 1 elif nums[begin] <= nums[next_bigger_pos]: next_bigger_pos -= 1 else: nums[next_smaller_pos] ^= nums[next_bigger_pos] nums[next_bigger_pos] ^= nums[next_smaller_pos] nums[next_smaller_pos] ^= nums[next_bigger_pos] next_smaller_pos += 1 nums[begin], nums[next_bigger_pos] = nums[next_bigger_pos], nums[begin] return next_bigger_pos def findKthLargest(self, nums, k): """ :type nums: List[int] :type k: int :rtype: int """ k = len(nums) - k begin, end = 0, len(nums) - 1 while True: position = self._quickSortHelper(nums, begin, end) if position == k: return nums[k] elif position < k: begin = position + 1 else: end = position - 1 # Should never be here. return 0]]>

Question Name: Shortest Palindrome

This is a variant of Longest Palindromic Substring. The key point is to convert the original question as following:

1. Shortest Palindrome by adding any characters in the head. Therefore the original string “original” is going to be “***original”. Because the inserted characters could be ANY thing, we can guarantee that the added sub-string is a mirror of the tailing sub-string. So the question turns out to be:

2. Convert the original string to a palindrome by removing characters at the end of it. Furthermore, it’s equal to:

3. In the original string, what’s the longest Palindrome sub-string, which starts at the index 0.

class Solution(object): def shortestPalindrome(self, s): """ :type s: str :rtype: str """ newS = "#"+"#".join(s)+"#" palHalfLenCenteringHere = [1] * len(newS) center, right = 0,1 for index in xrange(1, len(newS)): if index < right and index + palHalfLenCenteringHere[2*center-index] < right: # No way to extend the right. Thus the center is not changed. palHalfLenCenteringHere[index] = palHalfLenCenteringHere[2*center-index] else: # We MIGHT extend the right. And we DO change the center here. # IF we did NOT extend the right, the new center and old one have completely # the SAME effect on the next steps/rounds. center = index if index < right: # Use some pre-computed information palHalfLenCenteringHere[index] = right - center else: # Actually scanning from scratch palHalfLenCenteringHere[index] = 1 right += 1 # Try to extend the right while right < len(newS) and 2*center-right >= 0 and \ newS[right] == newS[2*center-right]: right += 1 palHalfLenCenteringHere[index] += 1 # Find the longest palindromic substring, which starts at the beginning. # We travel from the middle point to the begin point. Therefore the first # qualified sub-string is the longest one. for center in xrange(len(newS)//2, -1, -1): # Check whether this palindromic substring starts at index 0. if center - palHalfLenCenteringHere[center] + 1 == 0: halfLen = palHalfLenCenteringHere[center] result = newS[center + halfLen:][::-1] + newS return result.replace("#", "") # Should never be here. return None]]>