Daily Coding Problems 2023 Jan to Mar
- Daily Coding Problems
- Enviroment Setup
- Jan 1, 2023 LC 3 [Medium] Longest Substring Without Repeating Characters
- Jan 2, 2023 LC 23 [Hard] Merge k Sorted Lists
- Jan 3, 2023 LC 11 [Medium] Container With Most Water
- Jan 4, 2023 LC 22 [Medium] Generate Parentheses
- Jan 5, 2023 LC 19 [Medium] Remove Nth Node From End of List
- Jan 6, 2023 LC 24 [Medium] Swap Nodes in Pairs
- Jan 7, 2023 LC 31 [Medium] Next Permutation
- Jan 8, 2023 LC 33 [Medium] Search in Rotated Sorted Array
- Jan 9, 2023 LC 45 [Medium] Jump Game II
- Jan 10, 2023 LC 36 [Medium] Valid Sudoku
- Jan 11, 2023 LC 43 [Medium] Multiply Strings
- Jan 12, 2023 LC 46 [Medium] Permutations
- Jan 13, 2023 LC 47 [Medium] Permutations II
- Jan 14, 2023 LC 49 [Medium] Group Anagrams
- Jan 15, 2023 LC 39 [Medium] Combination Sum
- Jan 16, 2023 LC 40 [Medium] Combination Sum II
- Jan 17, 2023 LC 53 [Medium] Maximum Subarray
- Jan 18, 2023 LC 51 [Hard] N-Queens
- Jan 19, 2023 LC 54 [Medium] Spiral Matrix
Daily Coding Problems
Past Problems by Category: https://trsong.github.io/python/java/2019/04/02/DailyQuestionsByCategory.html
Enviroment Setup
Python 2.7 Playground: https://repl.it/languages/python
Python 3 Playground: https://repl.it/languages/python3
Java Playground: https://repl.it/languages/java
Jan 1, 2023 LC 3 [Medium] Longest Substring Without Repeating Characters
Question: Given a string s, find the length of the longest substring without repeating characters.
Example 1:
Input: s = "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Example 2:
Input: s = "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.
Example 3:
Input: s = "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Notice that the answer must be a substring, "pwke" is a subsequence and not a substring.
Solution: https://replit.com/@trsong/LC3-Longest-Substring-Without-Repeating-Characters#main.py
import unittest
def longest_uniq_substr(s):
last_occur_record = {}
res = 0
start = -1
for end, ch in enumerate(s):
last_occur = last_occur_record.get(ch, -1)
start = max(start, last_occur)
res = max(res, end - start)
last_occur_record[ch] = end
return res
class LongestUniqSubstrSpec(unittest.TestCase):
def testExample1(self):
s = "abcabcbb"
ans = 3 # "abc"
self.assertEqual(ans, longest_uniq_substr(s))
def testExample2(self):
s = "bbbbb"
ans = 1 # "b"
self.assertEqual(ans, longest_uniq_substr(s))
def testExample3(self):
s = "pwwkew"
ans = 3 # "wke"
self.assertEqual(ans, longest_uniq_substr(s))
def testEmptyString(self):
s = ""
ans = 0
self.assertEqual(ans, longest_uniq_substr(s))
def testLongestAtFront(self):
s = "abcdabcaba"
ans = 4 # "abcd"
self.assertEqual(ans, longest_uniq_substr(s))
def testLongestAtBack(self):
s = "aababcabcd"
ans = 4 # "abcd"
self.assertEqual(ans, longest_uniq_substr(s))
def testLongestInTheMiddle(self):
s = "aababcabcdabcaba"
ans = 4 # "abcd"
self.assertEqual(ans, longest_uniq_substr(s))
def testOneLetterString(self):
s = "a"
ans = 1
self.assertEqual(ans, longest_uniq_substr(s))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: not pruning invalid case. When tracking last occurrance, fail to consider an invalid case: “a” in “abcba” where “bcba” is not a valid result.
def longest_uniq_substr(s):
last_occur_record = {}
res = 0
for end, ch in enumerate(s):
last_occur = last_occur_record.get(ch, -1)
res = max(res, end - last_occur)
last_occur_record[ch] = end
return res
Rev 2: while updating map entry, removing an element is not necessary.
def longest_uniq_substr(s):
last_occur_record = {}
res = 0
start = 0
for end, ch in enumerate(s):
last_occur = last_occur_record.get(ch, -1)
while start < last_occur:
if s[start] in last_occur_record:
del last_occur_record[s[start]]
start += 1
res = max(res, end - start)
last_occur_record[ch] = end
return res
Rev 3: window start position is incorrect. Not consider 1 letter edge case
def longest_uniq_substr(s):
last_occur_record = {}
res = 0
start = 0
for end, ch in enumerate(s):
last_occur = last_occur_record.get(ch, -1)
start = max(start, last_occur)
res = max(res, end - start)
last_occur_record[ch] = end
return res
Jan 2, 2023 LC 23 [Hard] Merge k Sorted Lists
Question: You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.
Merge all the linked-lists into one sorted linked-list and return it.
Example 1:
Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
1->4->5,
1->3->4,
2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6
Example 2:
Input: lists = []
Output: []
Example 3:
Input: lists = [[]]
Output: []
Solution: https://replit.com/@trsong/LC-23-Merge-k-Sorted-Lists#main.py
import unittest
from queue import PriorityQueue
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def __eq__(self, other):
return str(self) == str(other)
def __str__(self):
return f"{self.val} -> {str(self.next)}"
def __repr__(self):
return str(self)
@classmethod
def fromList(cls, lst):
p = dummy = cls(-1)
for num in lst:
p.next = cls(num)
p = p.next
return dummy.next
def merge_k_sorted(num_lists):
pq = PriorityQueue()
for i, p in enumerate(num_lists):
if p is None:
continue
pq.put((p.val, i, p))
q = dummy = ListNode(-1)
while not pq.empty():
_, i, p = pq.get()
q.next = ListNode(p.val)
q = q.next
if p.next is not None:
pq.put((p.next.val, i, p.next))
return dummy.next
class MergeKSortedSpec(unittest.TestCase):
def testExample1(self):
num_lists = list(map(ListNode.fromList,
[[1, 4, 5], [1, 3, 4], [2, 6]]))
expected = ListNode.fromList([1, 1, 2, 3, 4, 4, 5, 6])
self.assertEqual(expected, merge_k_sorted(num_lists))
def testExample2(self):
num_lists = list(map(ListNode.fromList,[[]]))
expected = ListNode.fromList([])
self.assertEqual(expected, merge_k_sorted(num_lists))
def testExample3(self):
num_lists = list(map(ListNode.fromList,[]))
expected = ListNode.fromList([])
self.assertEqual(expected, merge_k_sorted(num_lists))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 3, 2023 LC 11 [Medium] Container With Most Water
Question: You are given an integer array height of length
n
. There aren
vertical lines drawn such that the two endpoints of thei
th line are(i, 0)
and(i, height[i])
.Find two lines that together with the x-axis form a container, such that the container contains the most water.
Return the maximum amount of water a container can store.
Notice that you may not slant the container.
Example 1:
Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.
Example 2:
Input: height = [1,1]
Output: 1
Solution: https://replit.com/@trsong/LC-11-Container-With-Most-Water#main.py
import unittest
def max_area(height):
lo = 0
hi = len(height) - 1
res = 0
while lo < hi:
area = min(height[lo], height[hi]) * (hi - lo)
res = max(res, area)
if height[lo] < height[hi]:
lo += 1
elif height[lo] > height[hi]:
hi -= 1
else:
lo += 1
hi -= 1
return res
class MaxAreaSpec(unittest.TestCase):
def testExample1(self):
self.assertEqual(49, max_area([1, 8, 6, 2, 5, 4, 8, 3, 7]))
def testExample2(self):
self.assertEqual(1, max_area([1, 1]))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: Do not be confused with LC 42 Trapping Rain Water. This question is about pruning impossible choices
Jan 4, 2023 LC 22 [Medium] Generate Parentheses
Question: Given
n
pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
Example 1:
Input: n = 3
Output: ["((()))","(()())","(())()","()(())","()()()"]
Example 2:
Input: n = 1
Output: ["()"]
Solution: https://replit.com/@trsong/LC-22-Generate-Parentheses#main.py
import unittest
def backtrack(res, accu, open_bal, close_bal):
if 0 == open_bal == close_bal:
res.append("".join(accu))
else:
if open_bal > 0:
accu.append("(")
backtrack(res, accu, open_bal - 1, close_bal)
accu.pop()
if close_bal > open_bal:
accu.append(")")
backtrack(res, accu, open_bal, close_bal - 1)
accu.pop()
def generate_parentheses(n):
if n <= 0:
return []
res = []
backtrack(res, [], n, n)
return res
class GenerateParentheseSpec(unittest.TestCase):
def testExample1(self):
expected = ["((()))","(()())","(())()","()(())","()()()"]
self.assertCountEqual(expected, generate_parentheses(3))
def testExample2(self):
expected = ["()"]
self.assertCountEqual(expected, generate_parentheses(1))
def testEmpty(self):
self.assertCountEqual([], generate_parentheses(0))
def testSize2(self):
expected = ["()()", "(())"]
self.assertCountEqual(expected, generate_parentheses(2))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: Did not handle edge case when
n
is0
which gives['']
instead of empty array.
Jan 5, 2023 LC 19 [Medium] Remove Nth Node From End of List
Question: Given the head of a linked list, remove the nth node from the end of the list and return its head.
Example 1:
Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
Example 2:
Input: head = [1], n = 1
Output: []
Example 3:
Input: head = [1,2], n = 1
Output: [1]
Solution: https://replit.com/@trsong/LC-19-Remove-Nth-Node-From-End-of-List#main.py
import unittest
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def __str__(self):
return f"{self.val} -> {str(self.next)}"
def __repr__(self):
return str(self)
def __eq__(self, other):
return str(self) == str(other)
@classmethod
def fromList(cls, lst):
p = dummy = cls(-1)
for num in lst:
p.next = cls(num)
p = p.next
return dummy.next
def remove_nth_from_end(head, n):
dummy = slow = fast = ListNode(-1, head)
for _ in range(n):
fast = fast.next
while fast and fast.next:
slow = slow.next
fast = fast.next
if slow.next:
slow.next = slow.next.next
return dummy.next
class RemoveNthFromEndSpec(unittest.TestCase):
def testExample1(self):
n, head = 2, ListNode.fromList([1, 2, 3, 4, 5])
expected = ListNode.fromList([1, 2, 3, 5])
self.assertEqual(expected, remove_nth_from_end(head, n))
def testExample2(self):
n, head = 1, ListNode.fromList([1])
expected = ListNode.fromList([])
self.assertEqual(expected, remove_nth_from_end(head, n))
def testExample3(self):
n, head = 1, ListNode.fromList([1, 2])
expected = ListNode.fromList([1])
self.assertEqual(expected, remove_nth_from_end(head, n))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 6, 2023 LC 24 [Medium] Swap Nodes in Pairs
Question: Given a linked list, swap every two adjacent nodes and return its head. You must solve the problem without modifying the values in the list’s nodes (i.e., only nodes themselves may be changed.)
Example 1:
Input: head = [1,2,3,4]
Output: [2,1,4,3]
Example 2:
Input: head = []
Output: []
Example 3:
Input: head = [1]
Output: [1]
Solution: https://replit.com/@trsong/LC-24-Swap-Nodes-in-Pairs#main.py
import unittest
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def __str__(self):
return f"{self.val} -> {str(self.next)}"
def __repr__(self):
return str(self)
def __eq__(self, other):
return str(self) == str(other)
@classmethod
def fromList(cls, lst):
p = dummy = cls(-1)
for num in lst:
p.next = cls(num)
p = p.next
return dummy.next
def swap_pairs(head):
p = dummy = ListNode(-1, head)
while p.next and p.next.next:
first = p.next
second = p.next.next
first.next = second.next
second.next = first
p.next = second
p = first
return dummy.next
class SwapPairsSpec(unittest.TestCase):
def testExample1(self):
head = ListNode.fromList([1, 2, 3, 4])
expected = ListNode.fromList([2, 1, 4, 3])
self.assertEqual(expected, swap_pairs(head))
def testExample2(self):
head = ListNode.fromList([1])
expected = ListNode.fromList([1])
self.assertEqual(expected, swap_pairs(head))
def testExample3(self):
head = ListNode.fromList([])
expected = ListNode.fromList([])
self.assertEqual(expected, swap_pairs(head))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 7, 2023 LC 31 [Medium] Next Permutation
Question: A permutation of an array of integers is an arrangement of its members into a sequence or linear order.
For example, for
arr = [1,2,3]
, the following are all the permutations of arr:[1,2,3]
,[1,3,2]
,[2, 1, 3]
,[2, 3, 1]
,[3,1,2]
,[3,2,1]
.The next permutation of an array of integers is the next lexicographically greater permutation of its integer. More formally, if all the permutations of the array are sorted in one container according to their lexicographical order, then the next permutation of that array is the permutation that follows it in the sorted container. If such arrangement is not possible, the array must be rearranged as the lowest possible order (i.e., sorted in ascending order).
For example, the next permutation of
arr = [1,2,3]
is[1,3,2]
.Similarly, the next permutation of
arr = [2,3,1]
is[3,1,2]
. While the next permutation ofarr = [3,2,1]
is[1,2,3]
because[3,2,1]
does not have a lexicographical larger rearrangement.Given an array of integers nums, find the next permutation of nums.
The replacement must be in place and use only constant extra memory.
Example 1:
Input: nums = [1,2,3]
Output: [1,3,2]
Example 2:
Input: nums = [3,2,1]
Output: [1,2,3]
Example 3:
Input: nums = [1,1,5]
Output: [1,5,1]
Solution: https://replit.com/@trsong/LC-31-Next-Permutation#main.py
import unittest
def next_permutation(nums):
prev_peak_pos = find_prev_peak_from_right(nums)
if prev_peak_pos < 0:
nums.reverse()
return
swap_pos = find_target_plus_from_right(nums, nums[prev_peak_pos])
nums[prev_peak_pos], nums[swap_pos] = nums[swap_pos], nums[prev_peak_pos]
reverse(nums, prev_peak_pos + 1, len(nums) - 1)
def find_prev_peak_from_right(nums):
for i in range(len(nums) - 2, -1, -1):
if nums[i] < nums[i + 1]:
return i
return -1
def find_target_plus_from_right(nums, target):
for i in range(len(nums) - 1, -1, -1):
if nums[i] > target:
return i
return -1
def reverse(nums, lo, hi):
while lo < hi:
nums[lo], nums[hi] = nums[hi], nums[lo]
lo += 1
hi -= 1
class NextPermutationSpec(unittest.TestCase):
def testExample1(self):
nums = [1, 2, 3]
expected = [1, 3, 2]
next_permutation(nums)
self.assertEqual(expected, nums)
def testExample2(self):
nums = [3, 2, 1]
expected = [1, 2, 3]
next_permutation(nums)
self.assertEqual(expected, nums)
def testExample3(self):
nums = [1, 1, 5]
expected = [1, 5, 1]
next_permutation(nums)
self.assertEqual(expected, nums)
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 8, 2023 LC 33 [Medium] Search in Rotated Sorted Array
Question: There is an integer array nums sorted in ascending order (with distinct values).
Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is
[nums[k], nums[k+1], ..., nums[n-1], nums[0]
,nums[1], ..., nums[k-1]]
(0-indexed). For example,[0,1,2,4,5,6,7]
might be rotated at pivot index 3 and become[4,5,6,7,0,1,2]
.Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or
-1
if it is not in nums.You must write an algorithm with
O(log n)
runtime complexity.
Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Example 2:
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1
Example 3:
Input: nums = [1], target = 0
Output: -1
Solution: https://replit.com/@trsong/LC-33-Search-in-Rotated-Sorted-Array#main.py
import unittest
def search(nums, target):
lo = 0
hi = len(nums) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
# case 1 & 2: value of mid < target while on same side
# case 3: value of mid > target while on differnt sides
if (nums[lo] <= nums[mid] < target or
nums[mid] < target <= nums[hi] or
target <= nums[hi] < nums[mid]):
lo = mid + 1
else:
hi = mid
return lo if nums[lo] == target else -1
class SearchSpec(unittest.TestCase):
def testExample1(self):
nums = [4, 5, 6, 7, 0, 1, 2]
target = 0
expected = 4
self.assertEqual(expected, search(nums, target))
def testExample2(self):
nums = [4, 5, 6, 7, 0, 1, 2]
target = 3
expected = -1
self.assertEqual(expected, search(nums, target))
def testExample3(self):
nums = [1]
target = 0
expected = -1
self.assertEqual(expected, search(nums, target))
def testFailed1(self):
nums = [1, 3]
target = 3
expected = 1
self.assertEqual(expected, search(nums, target))
def testFailed2(self):
nums = [5, 1, 3]
target = 3
expected = 2
self.assertEqual(expected, search(nums, target))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: Did not handle edge case when value of lo is equal to mid
def search(nums, target):
lo = 0
hi = len(nums) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
if (nums[lo] < nums[mid] < target or
nums[lo] < nums[mid] and nums[lo] > target):
lo = mid + 1
else:
hi = mid
return lo if nums[lo] == target else -1
Rev 2: did not handle case when mid and value on different sides
def search(nums, target):
lo = 0
hi = len(nums) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
if (nums[lo] <= nums[mid] < target or
nums[lo] <= nums[mid] and nums[lo] > target):
lo = mid + 1
else:
hi = mid
return lo if nums[lo] == target else -1
Jan 9, 2023 LC 45 [Medium] Jump Game II
Question: You are given a 0-indexed array of integers nums of length
n
. You are initially positioned atnums[0]
.Each element
nums[i]
represents the maximum length of a forward jump from indexi
. In other words, if you are atnums[i]
, you can jump to anynums[i + j]
where:
0 <= j <= nums[i]
andi + j < n
Return the minimum number of jumps to reach
nums[n - 1]
. The test cases are generated such that you can reachnums[n - 1]
.
Example 1:
Input: nums = [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [2,3,0,1,4]
Output: 2
Solution: https://replit.com/@trsong/LC-45-Jump-Game-II#main.py
import unittest
def jump(nums):
res = -1
max_pos = -1
potential_max_pos = -1
for pos, step in enumerate(nums):
if pos > max_pos:
res += 1
max_pos = potential_max_pos
local_max_pos = pos + step
potential_max_pos = max(potential_max_pos, local_max_pos)
return res
class JumpSpec(unittest.TestCase):
def testExample1(self):
self.assertEqual(2, jump([2, 3, 1, 1, 4]))
def testExample2(self):
self.assertEqual(2, jump([2, 3, 0, 1, 4]))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: violate the question setup: only increment when switch to new max position.
def jump(nums):
res = -1
max_pos = -1
for pos, step in enumerate(nums):
if pos > max_pos:
res += 1
local_max_pos = pos + step
max_pos = max(max_pos, local_max_pos)
return res
Jan 10, 2023 LC 36 [Medium] Valid Sudoku
Question: Determine if a
9 x 9
Sudoku board is valid. Only the filled cells need to be validated according to the following rules:
- Each row must contain the digits
1-9
without repetition.- Each column must contain the digits
1-9
without repetition.- Each of the nine
3 x 3
sub-boxes of the grid must contain the digits1-9
without repetition.Note:
- A Sudoku board (partially filled) could be valid but is not necessarily solvable.
- Only the filled cells need to be validated according to the mentioned rules.
Example 1:
Input: board =
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true
Example 2:
Input: board =
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: false
Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8's in the top left 3x3 sub-box, it is invalid.
Solution: https://replit.com/@trsong/LC-36-Valid-Sudoku#main.py
import unittest
N = 9
M = 3
def isValidSudoku(board):
for r in range(N):
row_it = (board[r][c] for c in range(N))
if not isValidSequence(row_it):
return False
for c in range(N):
col_it = (board[r][c] for r in range(N))
if not isValidSequence(col_it):
return False
for sub_r in range(M):
for sub_c in range(M):
sub_it = (board[r + sub_r * M][c + sub_c * M]
for r in range(M)
for c in range(M))
if not isValidSequence(sub_it):
return False
return True
def isValidSequence(it):
vec = 0
for ch in it:
if ch == '.':
continue
if not '0' <= ch <= '9':
return False
num = int(ch)
if vec & 1 << num:
return False
vec |= 1 << num
return True
class IsValidSudokuSpec(unittest.TestCase):
def testExample1(self):
board = [["5", "3", ".", ".", "7", ".", ".", ".", "."],
["6", ".", ".", "1", "9", "5", ".", ".", "."],
[".", "9", "8", ".", ".", ".", ".", "6", "."],
["8", ".", ".", ".", "6", ".", ".", ".", "3"],
["4", ".", ".", "8", ".", "3", ".", ".", "1"],
["7", ".", ".", ".", "2", ".", ".", ".", "6"],
[".", "6", ".", ".", ".", ".", "2", "8", "."],
[".", ".", ".", "4", "1", "9", ".", ".", "5"],
[".", ".", ".", ".", "8", ".", ".", "7", "9"]]
self.assertTrue(isValidSudoku(board))
def testExample2(self):
board = [["8", "3", ".", ".", "7", ".", ".", ".", "."],
["6", ".", ".", "1", "9", "5", ".", ".", "."],
[".", "9", "8", ".", ".", ".", ".", "6", "."],
["8", ".", ".", ".", "6", ".", ".", ".", "3"],
["4", ".", ".", "8", ".", "3", ".", ".", "1"],
["7", ".", ".", ".", "2", ".", ".", ".", "6"],
[".", "6", ".", ".", ".", ".", "2", "8", "."],
[".", ".", ".", "4", "1", "9", ".", ".", "5"],
[".", ".", ".", ".", "8", ".", ".", "7", "9"]]
self.assertFalse(isValidSudoku(board))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 11, 2023 LC 43 [Medium] Multiply Strings
Question: Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string.
Note: You must not use any built-in BigInteger library or convert the inputs to integer directly.
Example 1:
Input: num1 = "2", num2 = "3"
Output: "6"
Example 2:
Input: num1 = "123", num2 = "456"
Output: "56088"
Solution: https://replit.com/@trsong/LC-43-Multiply-Strings#main.py
import unittest
def multiply(num1, num2):
if len(num1) < len(num2):
num1, num2 = num2, num1
rev_num1 = list(map(int, num1))
rev_num1.reverse()
rev_num2 = list(map(int, num2))
rev_num2.reverse()
reverse_res = multiply_reverse_arr(rev_num1, rev_num2)
return formate_reverse_arr(reverse_res)
def multiply_reverse_arr(nums1, nums2):
n, m = len(nums1), len(nums2)
res = [0] * (n + m)
for i in range(n):
for j in range(m):
res[j + i] += nums1[i] * nums2[j]
carry = 0
for i in range(n + m):
res[i] += carry
carry = res[i] // 10
res[i] %= 10
return res
def formate_reverse_arr(nums):
res = []
skipped = True
for i in range(len(nums) - 1, -1, -1):
if skipped and nums[i] == 0:
continue
skipped = False
res.append(str(nums[i]))
return ''.join(res) or "0"
class MultiplySpec(unittest.TestCase):
def testExample1(self):
num1 = "2"
num2 = "3"
expected = "6"
self.assertEqual(expected, multiply(num1, num2))
def testExample2(self):
num1 = "123"
num2 = "456"
expected = "56088"
self.assertEqual(expected, multiply(num1, num2))
def testFailedRev1(self):
num1 = num2 = expected = "0"
self.assertEqual(expected, multiply(num1, num2))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: Did not consider when result is “0”
"""
Omit the other parts
"""
def formate_reverse_arr(nums):
res = []
skipped = True
for i in range(len(nums) - 1, -1, -1):
if skipped and nums[i] == 0:
continue
skipped = False
res.append(str(nums[i]))
return ''.join(res) # <----- what if res is empty
Jan 12, 2023 LC 46 [Medium] Permutations
Question: Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.
Example 1:
Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Example 2:
Input: nums = [0,1]
Output: [[0,1],[1,0]]
Example 3:
Input: nums = [1]
Output: [[1]]
Solution: https://replit.com/@trsong/LC-46-Permutations#main.py
import unittest
def permute(nums):
res = []
backtrack(nums, res, 0)
return res
def backtrack(nums, res, cur_pos):
n = len(nums)
if cur_pos >= n:
res.append(nums[:])
else:
for swap_pos in range(cur_pos, n):
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
backtrack(nums, res, cur_pos + 1)
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
class PermuteSpec(unittest.TestCase):
def testExample1(self):
nums = [1, 2, 3]
expected = [
[1, 2, 3],
[1, 3, 2],
[2, 1, 3],
[2, 3, 1],
[3, 1, 2],
[3, 2, 1]
]
self.assertCountEqual(expected, permute(nums))
def testExample2(self):
nums = [0, 1]
expected = [
[0, 1],
[1, 0]
]
self.assertCountEqual(expected, permute(nums))
def testExample3(self):
nums = [1]
expected = [
[1]
]
self.assertCountEqual(expected, permute(nums))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 13, 2023 LC 47 [Medium] Permutations II
Question: Given a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order.
Example 1:
Input: nums = [1,1,2]
Output:
[[1,1,2],
[1,2,1],
[2,1,1]]
Example 2:
Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Less Optimal Yet Acceptable Solution: https://replit.com/@trsong/LC-47-Permutations-II#main.py
def permute_unique(nums):
res = []
backtrack(nums, res, 0)
return res
def backtrack(nums, res, cur_pos):
n = len(nums)
if cur_pos >= n:
res.append(nums[:])
else:
seen = set()
for swap_pos in range(cur_pos, n):
if nums[swap_pos] in seen:
continue
seen.add(nums[swap_pos])
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
backtrack(nums, res, cur_pos + 1)
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
Optimal Solution: https://replit.com/@trsong/LC-47-Permutations-II-optimal-solution#main.py
import unittest
def permute_unique(nums):
histogram = generate_histogram(nums)
res = []
backtrack(len(nums), [], res, histogram)
return res
def backtrack(remain_step, accu, res, histogram):
if remain_step == 0:
res.append(accu[:])
else:
for num, count in histogram.items():
if count <= 0:
continue
accu.append(num)
histogram[num] -= 1
backtrack(remain_step - 1, accu, res, histogram)
accu.pop()
histogram[num] += 1
def generate_histogram(nums):
histogram = {}
for num in nums:
histogram[num] = histogram.get(num, 0) + 1
return histogram
class PermuteUniqueSpec(unittest.TestCase):
def testExample1(self):
nums = [1, 1, 2]
expected = [[1, 1, 2], [1, 2, 1], [2, 1, 1]]
self.assertCountEqual(expected, permute_unique(nums))
def testExample2(self):
nums = [1, 2, 3]
expected = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2],
[3, 2, 1]]
self.assertCountEqual(expected, permute_unique(nums))
def testFailRev2(self):
nums = [0, 1, 0, 0, 9]
expected = [[0, 0, 0, 1, 9], [0, 0, 0, 9, 1], [0, 0, 1, 0, 9],
[0, 0, 1, 9, 0], [0, 0, 9, 0, 1], [0, 0, 9, 1, 0],
[0, 1, 0, 0, 9], [0, 1, 0, 9, 0], [0, 1, 9, 0, 0],
[0, 9, 0, 0, 1], [0, 9, 0, 1, 0], [0, 9, 1, 0, 0],
[1, 0, 0, 0, 9], [1, 0, 0, 9, 0], [1, 0, 9, 0, 0],
[1, 9, 0, 0, 0], [9, 0, 0, 0, 1], [9, 0, 0, 1, 0],
[9, 0, 1, 0, 0], [9, 1, 0, 0, 0]]
self.assertCountEqual(expected, permute_unique(nums))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: Neither space nor time complexity is ideal due to set add and lookup
def permute_unique(nums):
res = []
backtrack(nums, res, 0)
return res
def backtrack(nums, res, cur_pos):
n = len(nums)
if cur_pos >= n:
res.append(nums[:])
else:
seen = set()
for swap_pos in range(cur_pos, n):
if nums[swap_pos] in seen:
continue
seen.add(nums[swap_pos])
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
backtrack(nums, res, cur_pos + 1)
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
Rev 2: Sort and skip duplicate number won’t work either. Because swap position will change relative order. Below failed the test case:
[0, 1, 0, 0, 9]
def permute_unique(nums):
res = []
nums.sort()
backtrack(nums, res, 0)
return res
def backtrack(nums, res, cur_pos):
n = len(nums)
if cur_pos >= n:
res.append(nums[:])
else:
for swap_pos in range(cur_pos, n):
if swap_pos > cur_pos and nums[swap_pos] == nums[swap_pos - 1]:
continue
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
backtrack(nums, res, cur_pos + 1)
nums[swap_pos], nums[cur_pos] = nums[cur_pos], nums[swap_pos]
Jan 14, 2023 LC 49 [Medium] Group Anagrams
Question: Given an array of strings strs, group the anagrams together. You can return the answer in any order.
An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
Example 1:
Input: strs = ["eat","tea","tan","ate","nat","bat"]
Output: [["bat"],["nat","tan"],["ate","eat","tea"]]
Example 2:
Input: strs = [""]
Output: [[""]]
Example 3:
Input: strs = ["a"]
Output: [["a"]]
Solution: https://replit.com/@trsong/LC-49-Group-Anagrams#main.py
import unittest
P = 101
CHAR_WEIGHT = {}
def group_anagram(strs):
grouby_hash = {}
for s in strs:
hash_code = hash_anagram(s)
grouby_hash[hash_code] = grouby_hash.get(hash_code, [])
grouby_hash[hash_code].append(s)
return list(grouby_hash.values())
def hash_anagram(s):
global CHAR_WEIGHT
histogram = generate_histogram(s)
res = 0
for ch, count in histogram.items():
if ch not in CHAR_WEIGHT:
CHAR_WEIGHT[ch] = P ** (ord(ch) - ord('a'))
res += CHAR_WEIGHT[ch] * count
return res
def generate_histogram(s):
histogram = {}
for ch in s:
histogram[ch] = histogram.get(ch, 0) + 1
return histogram
class GroupAnagram(unittest.TestCase):
def assertResult(self, expected, res):
format = lambda strs: list(map(lambda words: repr(sorted(words)), strs))
self.assertCountEqual(format(expected), format(res))
def testExample1(self):
strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
expected = [["bat"], ["nat", "tan"], ["ate", "eat", "tea"]]
self.assertResult(expected, group_anagram(strs))
def testExample2(self):
strs = [""]
expected = [[""]]
self.assertResult(expected, group_anagram(strs))
def testExample3(self):
strs = ["a"]
expected = [["a"]]
self.assertResult(expected, group_anagram(strs))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 15, 2023 LC 39 [Medium] Combination Sum
Question: Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.
The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.
The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input.
Example 1:
Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.
Example 2:
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]
Example 3:
Input: candidates = [2], target = 1
Output: []
Solution: https://replit.com/@trsong/LC-39-Combination-Sum#main.py
import unittest
def combination_sum(candidates, target):
sorted_desc_candidates = list(sorted(set(candidates), reverse=True))
res = []
backtrack(target, res, [], sorted_desc_candidates, 0)
return res
def backtrack(balance, res, accu, sorted_desc_candidates, cur_candidate_index):
if balance == 0:
res.append(accu[:])
else:
for i in range(cur_candidate_index, len(sorted_desc_candidates)):
num = sorted_desc_candidates[i]
if num > balance:
continue
accu.append(num)
backtrack(balance - num, res, accu, sorted_desc_candidates, i)
accu.pop()
class CombinationSumSpec(unittest.TestCase):
def assertResult(self, expected, res):
format = lambda lon: list(
map(lambda nums: repr(list(sorted(nums))), lon))
self.assertCountEqual(format(expected), format(res))
def testExample1(self):
candidates = [2, 3, 6, 7]
target = 7
expected = [[2, 2, 3], [7]]
self.assertResult(expected, combination_sum(candidates, target))
def testExample2(self):
candidates = [2, 3, 5]
target = 8
expected = [[2, 2, 2, 2], [2, 3, 3], [3, 5]]
self.assertResult(expected, combination_sum(candidates, target))
def testExample3(self):
candidates = [2]
target = 1
expected = []
self.assertResult(expected, combination_sum(candidates, target))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: space complexity is not optimal as recursive step involving slicing the candiate into sub arrays
def combination_sum(candidates, target):
sorted_desc_candidates = list(sorted(set(candidates), reverse=True))
res = []
backtrack(target, res, [], sorted_desc_candidates)
return res
def backtrack(balance, res, accu, sorted_desc_candidates):
if balance == 0:
res.append(accu[:])
else:
for i, num in enumerate(sorted_desc_candidates):
if num > balance:
continue
accu.append(num)
backtrack(balance - num, res, accu, sorted_desc_candidates[i:])
accu.pop()
Jan 16, 2023 LC 40 [Medium] Combination Sum II
Question: Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target.
Each number in candidates may only be used once in the combination.
Note: The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
Example 2:
Input: candidates = [2,5,2,1,2], target = 5
Output:
[
[1,2,2],
[5]
]
Solution: https://replit.com/@trsong/LC-40-Combination-Sum-II#main.py
import unittest
def combination_sum(candidates, target):
candidate_value_rank = list(sorted(set(candidates), reverse=True))
candidate_histogram = generate_histogram(candidates)
res = []
backtrack(target, res, [], candidate_value_rank, 0, candidate_histogram)
return res
def backtrack(balance, res, accu, candidate_value_rank, candidate_value_index, candidate_histogram):
if balance == 0:
res.append(accu[:])
else:
for i in range(candidate_value_index, len(candidate_value_rank)):
num = candidate_value_rank[i]
if num > balance or candidate_histogram[num] <= 0:
continue
accu.append(num)
candidate_histogram[num] -= 1
backtrack(balance - num, res, accu, candidate_value_rank, i, candidate_histogram)
candidate_histogram[num] += 1
accu.pop()
def generate_histogram(nums):
histogram = {}
for num in nums:
histogram[num] = histogram.get(num, 0) + 1
return histogram
class CombinationSumSpec(unittest.TestCase):
def assertResult(self, expected, res):
format = lambda lon: list(
map(lambda nums: repr(list(sorted(nums))), lon))
self.assertCountEqual(format(expected), format(res))
def testExample1(self):
candidates = [10, 1, 2, 7, 6, 1, 5]
target = 8
expected = [[1, 1, 6], [1, 2, 5], [1, 7], [2, 6]]
self.assertResult(expected, combination_sum(candidates, target))
def testExample2(self):
candidates = [2, 5, 2, 1, 2]
target = 5
expected = [[1, 2, 2], [5]]
self.assertResult(expected, combination_sum(candidates, target))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: Failed the following case due to unable to differentiate which
1
to choose
target, candidate = 3, [2, 1, 1]
res = [[2, 1], [2, 1]]
def combination_sum(candidates, target):
sorted_desc_candidates = list(sorted(candidates, reverse=True))
res = []
backtrack(target, res, [], sorted_desc_candidates, 0)
return res
def backtrack(balance, res, accu, sorted_desc_candidates, cur_candidate_index):
if balance == 0:
res.append(accu[:])
else:
for i in range(cur_candidate_index, len(sorted_desc_candidates)):
num = sorted_desc_candidates[i]
if num > balance:
continue
accu.append(num)
backtrack(balance - num, res, accu, sorted_desc_candidates, i + 1)
accu.pop()
Jan 17, 2023 LC 53 [Medium] Maximum Subarray
Question: Given an integer array nums, find the subarray with the largest sum, and return its sum.
Example 1:
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: The subarray [4,-1,2,1] has the largest sum 6.
Example 2:
Input: nums = [1]
Output: 1
Explanation: The subarray [1] has the largest sum 1.
Example 3:
Input: nums = [5,4,-1,7,8]
Output: 23
Explanation: The subarray [5,4,-1,7,8] has the largest sum 23.
Solution: https://replit.com/@trsong/LC-53-Maximum-Subarray#main.py
import unittest
def cal_max_sub_arr_sum(nums):
max_sum = 0
max_sum_so_far = 0
for num in nums:
max_sum_so_far = max(0, max_sum_so_far) + num
max_sum = max(max_sum_so_far, max_sum)
return max_sum
class CalMaxSubArrSumSpec(unittest.TestCase):
def testExample1(self):
nums = [-2,1,-3,4,-1,2,1,-5,4]
expected = 6
self.assertEqual(expected, cal_max_sub_arr_sum(nums))
def testExample2(self):
nums = [1]
expected = 1
self.assertEqual(expected, cal_max_sub_arr_sum(nums))
def testExample3(self):
nums = [5,4,-1,7,8]
expected = 23
self.assertEqual(expected, cal_max_sub_arr_sum(nums))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: max_sum_so_far represents max dp ended at position thus should always include
num
def cal_max_sub_arr_sum(nums):
max_sum = 0
max_sum_so_far = 0
for num in nums:
max_sum_so_far = max(0, max_sum_so_far + num)
max_sum = max(max_sum_so_far, max_sum)
return max_sum
Rev 2: Failed edge case when
nums = [-1]
should return-1
def cal_max_sub_arr_sum(nums):
max_sum = 0
max_sum_so_far = 0
for num in nums:
max_sum_so_far = num + max(0, max_sum_so_far)
max_sum = max(max_sum_so_far, max_sum)
return max_sum
Jan 18, 2023 LC 51 [Hard] N-Queens
Question: The n-queens puzzle is the problem of placing
n
queens on ann x n
chessboard such that no two queens attack each other.Given an integer
n
, return all distinct solutions to the n-queens puzzle. You may return the answer in any order.Each solution contains a distinct board configuration of the n-queens’ placement, where
'Q'
and'.'
both indicate a queen and an empty space, respectively.
Example 1:
Input: n = 4
Output: [
[
".Q..",
"...Q",
"Q...",
"..Q."
], [
"..Q.",
"Q...",
"...Q",
".Q.."
]
]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above
Example 2:
Input: n = 1
Output: [["Q"]]
Solution: https://replit.com/@trsong/LC-51-N-Queens#main.py
import unittest
def solve_n_queen(n):
visited_column = [False] * n
visited_diagonal1 = [False] * (2 * n)
visited_diagonal2 = [False] * (2 * n)
res = []
rows = [None] * n
def backtrack(r):
if r >= n:
res.append(format_grid(rows))
else:
for c in range(n):
d1 = r + c
d2 = n + r - c
if visited_column[c] or visited_diagonal1[d1] or visited_diagonal2[d2]:
continue
visited_column[c] = True
visited_diagonal1[d1] = True
visited_diagonal2[d2] = True
rows[r] = c
backtrack(r + 1)
rows[r] = None
visited_column[c] = False
visited_diagonal1[d1] = False
visited_diagonal2[d2] = False
backtrack(0)
return res
def format_grid(columns):
n = len(columns)
rows = []
for r, c in enumerate(columns):
rows.append('.' * c + 'Q' + '.' * (n - 1 - c))
return rows
class SolveNQueenSpec(unittest.TestCase):
def assertResult(self, expected, res):
display_grid = lambda g: '\n' + '\n'.join(g) + '\n'
formated_expected = set(map(display_grid, expected))
formated_res = set(map(display_grid, res))
if formated_expected != formated_res:
print("The following is missing")
for g in formated_expected - formated_res:
print(g)
print("The following is unexpected:")
for g in formated_res - formated_expected:
print(g)
self.assertEqual(formated_expected, formated_res)
def testExample1(self):
n = 4
expected = [[
".Q..",
"...Q",
"Q...",
"..Q."
], [
"..Q.",
"Q...",
"...Q",
".Q.."
]]
self.assertResult(expected, solve_n_queen(n))
def testExample2(self):
n = 1
expected = [["Q"]]
self.assertResult(expected, solve_n_queen(n))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Jan 19, 2023 LC 54 [Medium] Spiral Matrix
Question: Given an
m x n
matrix, return all elements of the matrix in spiral order.
Example 1:
Input: matrix = [
[1,2,3],
[4,5,6],
[7,8,9]
]
Output: [1,2,3,6,9,8,7,4,5]
Example 2:
Input: matrix = [
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]
Solution: https://replit.com/@trsong/LC-54-Spiral-Matrix#main.py
import unittest
def spiral_order(matrix):
if not matrix or not matrix[0]:
return []
r_lo = 0
c_lo = 0
r_hi = len(matrix) - 1
c_hi = len(matrix[0]) - 1
res = []
while r_lo <= r_hi and c_lo <= c_hi:
r = r_lo
c = c_lo
# left -> right
while c < c_hi:
res.append(matrix[r][c])
c += 1
c_hi -= 1
# top -> bottom
while r < r_hi:
res.append(matrix[r][c])
r += 1
r_hi -= 1
if r_lo > r_hi or c_lo > c_hi:
res.append(matrix[r][c])
break
# right -> left
while c > c_lo:
res.append(matrix[r][c])
c -= 1
c_lo += 1
# bottom -> top
while r > r_lo:
res.append(matrix[r][c])
r -= 1
r_lo += 1
return res
class SpiralOrderSpec(unittest.TestCase):
def testExample1(self):
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
expected = [1, 2, 3, 6, 9, 8, 7, 4, 5]
self.assertEqual(expected, spiral_order(matrix))
def testExample2(self):
matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
expected = [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]
self.assertEqual(expected, spiral_order(matrix))
def testFailed1(self):
matrix = [
[2, 5],
[8, 4],
[0, -1]
]
expected = [2, 5, 4, -1, 0, 8]
self.assertEqual(expected, spiral_order(matrix))
if __name__ == '__main__':
unittest.main(exit=False, verbosity=2)
Failed Attempts:
Rev 1: Be cautious about while loop condition,
and
oror
def spiral_order(matrix):
if not matrix or not matrix[0]:
return []
r_lo = 0
c_lo = 0
r_hi = len(matrix) - 1
c_hi = len(matrix[0]) - 1
res = []
while r_lo <= r_hi or c_lo <= c_hi:
...
return res