LeetCode [220/221]

2020/12/29

41 - First Missing Positive

Problem

Given an unsorted integer array, find the smallest missing positive integer.

Example 1:

Input: [1,2,0]
Output: 3

Example 2:

Input: [3,4,-1,1]
Output: 2

Example 3:

Input: [7,8,9,11,12]
Output: 1

Note:

Your algorithm should run in O(n) time and uses constant extra space.

Notes

Run in O(n) time and uses constant extra space

  1. Say the length of the array is l, the number must be in 1…l+1 (also l possible numbers)

    For example

    [1, 2, 3, 4], the first missing positive is 5.

    [7, 8, 9, 10], the first missing positive is 1

    It means you can use the array as a constant space. The result must be (one of the indexes + 1).

  2. We put the number in the right place. When it is 10, we swap it with A[9].

After all the numbers are in the right place, the first one, whose index + 1 != number, it is the missing one

How to put the numer in the right place

Use the `while` to swap the numbers. Only `if` can not do the same job.

Consider nums = [3, 4, -1, 1].

Only with if:

First Loop: swap 3 and -1

nums = [-1, 4, 3, 1]

Second Loop: swap 4 and 1

nums = [-1, 1, 3, 4]

And the process stops. Because 4 is already in the right place. You miss to put the 1 in the right place.

So you have to do it recursively, with `while`.

Solution

class Solution(object):
    def firstMissingPositive(self, nums):
        l = len(nums)
        for i in range(l):
            # Note!: here has to be using while
            while (nums[i] > 0 and nums[i] <= l and nums[nums[i] - 1] != nums[i]):
                nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]

        for i, n in enumerate(nums):
            if (n != i+1):
                return i + 1

        return l + 1

48 - Rotate Image

leetcode

Problem

You are given an n x n 2D matrix representing an image.

Rotate the image by 90 degrees (clockwise).

Note:

You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.

Example 1:

Given input matrix =
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

rotate the input matrix in-place such that it becomes:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]
Example 2:

Given input matrix =
[
  [ 5, 1, 9,11],
  [ 2, 4, 8,10],
  [13, 3, 6, 7],
  [15,14,12,16]
],

rotate the input matrix in-place such that it becomes:
[
  [15,13, 2, 5],
  [14, 3, 4, 1],
  [12, 6, 8, 9],
  [16, 7,10,11]
]

Notes

Naive solution, to do it one by one.

Important:

You go from the outside into the middle. So the main loop is half of the dimension.

The inner loop should also shrink its size everytime. Begins at i and ends and n-2-i, not n-1-i.

Because you don’t want to swap the last one. The last one n-1-i has already been swapped with the i.

Another solution: how to rotate a matrix faster

Swap the diagnoal elements and reverse each line in the matrix.

1 2 3 swap 1 4 7 reverse 7 4 1
4 5 6 —> 2 5 8 ——> 8 5 2
7 8 9 3 6 9 9 6 3

Solution

Solution 1: Straightforward

class Solution(object):
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix)

        for i in range(n//2):
            # Shrink the dimension
            # Do not include the last element
            for j in range(i, n-i-1):
                tmp = matrix[i][j]
                matrix[i][j] = matrix[n-1-j][i]
                matrix[n-1-j][i] = matrix[n-1-i][n-1-j]
                matrix[n-1-i][n-1-j] = matrix[j][n-1-i]
                matrix[j][n-1-i] = tmp

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Solution().rotate(matrix)
[print(*line) for line in matrix]

Solution 2:

class Solution(object):
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix)

        for i in range(n):
            for j in range(i, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

        for i in range(n):
            matrix[i].reverse()

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Solution().rotate(matrix)
[print(*line) for line in matrix]

53 - Maximum Subarray

leetcode

Problem

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:

Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.

Notes

Dynamic programming problem.

Use nums[i] always store the maximum sum.

maxSum(i) = maxSum(i-1) + nums[i] only if maxSum(i-1) > 0

Solution

Solution 1: use a extra dp array

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        curSum = maxSum = nums[0]

        for num in nums[1:]:
          curSum = max(num, curSum+num)
          maxSum = max(curSum, maxSum)

        return maxSum

print(Solution().maxSubArray([-2,1,-3,4,-1,2,1,-5,4]))

Solution 2: no extra space, in place modify

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """

        if len(nums) == 0:
            return 0

        ret = nums[0]

        for i in range(1, len(nums)):
            if nums[i - 1] > 0:
                nums[i] += nums[i - 1]

            ret = max(ret, nums[i])

        return ret
print(Solution().maxSubArray([-2,1,-3,4,-1,2,1,-5,4]))

55 - Jump Game

leetcode

Problem

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
             jump length is 0, which makes it impossible to reach the last index.

Notes

Greedy algorithm. There are 2 approaches, from head or from tail.

Start from head

always remember the furthest reachable index.

reach = max(i + nums[i], reach) if i <= reach

Start from tail

always remember the last position it can reach.

lastPos = i if i + nums[i] >= lastPos

Solition

Solution 1: start from head

class Solution():
    def canJump(self, nums):
        reach = 0

        for i in range(len(nums)):
            if i <= reach:
                reach = max(i + nums[i], reach)
            else:
              break

        return reach >= len(nums) - 1

print(Solution().canJump([ 2,3,1,1,4 ]))
print(Solution().canJump([ 3,2,1,0,4 ] ))

Solution 2: start from tail

class Solution():
    def canJump(self, nums):
        lastPos = len(nums) - 1
        for i in reversed(range(len(nums))):
            if i + nums[i] >= lastPos:
                lastPos = i

        return lastPos == 0

print(Solution().canJump([ 2,3,1,1,4 ]))
print(Solution().canJump([ 3,2,1,0,4 ] ))

62 - Unique Paths

leetcode

Problem

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?

Note: m and n will be at most 100.

Example 1:

Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right
Example 2:

Input: m = 7, n = 3
Output: 28

Notes

It is a DP problem.

  1. There are only two possibilities to arrive at the finish point

    1. arrive at that point from above
    2. arrive at that point from left
  2. So the ways to arrive at current point is equal to the ways from above plus the ways from left. dp[i][j] = dp[i][j-1] + dp[i - 1][j]

  3. Dynamic programming. Maintain a two dimensional matrix.

  4. Optimize it to one dimension.

  5. Complexity O(m * n)

UniquePath with 2-D DP

dp[i][j] = dp[i-1][j] + dp[i][j-1]

After you have the edge, you go levelly to the bottom.

UniquePath with 1-D DP

dp[j] = dp[j - 1] + dp[j]

Solution

Solution 1: 2-D DP

class Solution():
    def uniquePath(self, m, n):
        dp = [[1 for j in range(n)] for i in range(m)]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = dp[i][j - 1] + dp[i - 1][j]

        return dp[-1][-1] if m and n else 0

Solution 2: 1-D DP

class Solution():
    def uniquePath(self, m, n):
        dp = [1] * n

        for i in range(1, m):
            for j in range(1, n):
                dp[j] = dp[j - 1] + dp[j]

        return dp[-1] if m and n else 0

64 - Minimum Path Sum

leetcode

Problem

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Example:

Input:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.

Notes

Thinking: It seems to be a greedy algorithm problem.

It is a dp problem.

dp equation:

dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + grid[i][j]

Remember to handle the edge cases.

Solution

Solution 1: 2D DP

class Solution(object):
    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m = len(grid)
        n = len(grid[0])

        for i in range(m):
            for j in range(n):
                if i == 0:
                    if j == 0:
                        continue
                    else:
                        grid[i][j] += grid[i][j-1]
                else:
                    if j == 0:
                        grid[i][j] += grid[i-1][j]
                    else:
                        grid[i][j] += min(grid[i-1][j], grid[i][j-1])
        return grid[-1][-1] if m and n else 0


grid = [[1,3,1],[1,5,1],[4,2,1]]

print(Solution().minPathSum(grid))
import sys
class Solution(object):
    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m = len(grid)
        n = len(grid[0])

        dp = [[ 0 for i in range(n+1)] for j in range(m+1)]

        for i in range(len(dp)):
          dp[i][0] = sys.maxsize

        for i in range(len(dp[0])):
            dp[0][i] = sys.maxsize

        dp[1][1] = grid[0][0]

        for i in range(1, m+1):
            for j in range(1, n+1):
                if i == 1 and j == 1:
                    continue
                else:
                    dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i-1][j-1]


        return dp[-1][-1] if m and n else 0


grid = [[1,3,1],[1,5,1],[4,2,1]]

print(Solution().minPathSum(grid))

Solution 2: 1D DP

import sys


class Solution(object):
    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m = len(grid)
        n = len(grid[0])

        dp = [sys.maxsize for i in range(n + 1)]
        dp[1] = grid[0][0]

        for i in range(m):
            for j in range(n):
                if i == 0 and j == 0:
                    continue
                else:
                    dp[j + 1] = min(dp[j + 1], dp[j]) + grid[i][j]

        return dp[-1] if m and n else 0


grid = [[1, 3, 1], [1, 5, 1], [4, 2, 1]]

print(Solution().minPathSum(grid))

70 - Climbing Stairs

leetcode

Problem

You are climbing a stair case. It takes n steps to reach to the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Note: Given n will be a positive integer.

Example 1:

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
Example 2:

Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

Notes

The distinct ways to take n stair cases:

  1. take one step at last, the distinct ways to take n-1 stair cases -> f(n-1) ways
  2. take two steps at last, the distinct ways to take n-2 stair cases -> f(n-2) ways

So f(n) = f(n-1) + f(n-2)

Solution

class Solution():
    def climbStairs(self, n):
        if n < 2:
            return n

        dp = [0] * n

        dp[0] = 1
        dp[1] = 2

        for i in range(2, n):
            dp[i] = dp[i-1] + dp[i-2]

        return dp[-1]

91 - Decode Ways

leetcode

Problem

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26
Given a non-empty string containing only digits, determine the total number of ways to decode it.

Example 1:

Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).
Example 2:

Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

Notes

DP problem.

  1. Initialize dp array with dp[0] = 1 as padding, the rest of them are 0.
  2. Start from the first index of the string.
    1. If dp[i] is in range 1 to 9, dp[i] = dp[i-1]. The ways of decode will not increase, if it is 0, it remains 0.
    2. If dp[i] is in range 10 to 26, dp[i] += dp[i-1]. The ways of decode increase by one, if it is 00, it remains 0.

Important:

  1. Corner cases: “0” -> 0, “1002” -> 0
  2. Notice the padding

Solution

class Solution():
    def numsDecodings(self, s):

        if not s:
          return 0

        n = len(s)

        dp = [0] * (n + 1)

        dp[0] = 1

        for i in range(1, n+1):

            if s[i-1: i] != '0':
                dp[i] = dp[i-1]

            if i != 1 and '10' <= s[i-2:i] <= '26':
                dp[i] += dp[i-2]

        return dp[-1]

509 - Fibonacci Number

leetcode

Problem

The Fibonacci numbers, commonly denoted F(n) form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. That is,

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), for N > 1.
Given N, calculate F(N).



Example 1:

Input: 2
Output: 1
Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1.
Example 2:

Input: 3
Output: 2
Explanation: F(3) = F(2) + F(1) = 1 + 1 = 2.
Example 3:

Input: 4
Output: 3
Explanation: F(4) = F(3) + F(2) = 2 + 1 = 3.

Notes

DP Problem.

Important:

Note how long is the dp array. It shoud be N+1, since we start with the number 0.

Solution

class Solution(object):
    def fib(self, N):
        """
        :type N: int
        :rtype: int
        """
        if N < 2:
            return N

        dp = [0] * (N + 1)

        dp[0] = 0
        dp[1] = 1

        for i in range(2, N + 1):
            dp[i] = dp[i - 1] + dp[i - 2]

        return dp[-1]

75 - Sort Colors

leetcode

Problem

Given an array with n objects colored red, white or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white and blue.

Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.

Note: You are not suppose to use the library's sort function for this problem.

Example:

Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
Follow up:

A rather straight forward solution is a two-pass algorithm using counting sort.
First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's.
Could you come up with a one-pass algorithm using only constant space?

Notes

First attempt is to use two pointer.

There is a but a corner case: when two point

class Solution:

    def subsets(self, nums):
        res = [[]]
        for i in sorted(nums):
            res += [item+[i] for item in res]

        return res

print(Solution().subsets([1, 2, 3]))

78 - Subsets

Problem

Given a set of distinct integers, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: nums = [1,2,3]
Output:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

Solution

Solution 1: DFS

class Solution(object):
    def subsets(self, nums):
        res = []
        self.dfs(sorted(nums), 0, [], res)
        return res

    def dfs(self, nums, index, path, res):
        res.append(path)
        for i in range(index, len(nums)):
            self.dfs(nums, i + 1, path + [nums[i]], res)

Solution 2: Iterative

class Solution:
    def subsets(self, nums):
        res = [[]]
        for i in sorted(nums):
            res += [item+[i] for item in res]
        return res

Solution 3: backtrack


class Solution(object):

    def subsets(self, nums):
        output = []
        for k in range(0, len(nums)+1):
            temp = []
            self.backtrack(0, k, nums, temp, output)

        return output

    def backtrack(self, begin, length, nums, temp, output):

        if length == len(temp):
            output.append(temp[:])

        for i in range(begin, len(nums)):
            temp.append(nums[i])
            self.backtrack(i+1, length, nums, temp, output)
            temp.pop()


print(Solution().subsets([1, 2, 3]))

Solution 4: bitmask


class Solution():
  def subsets(self, nums):
    n = len(nums)
    output = []
    for i in range(2**n, 2**(n+1)):
      bitmask = bin(i)[3:]
      output.append([nums[i] for i in range(n) if bitmask[i] == '1' ])

    return output

leetcode

Problem

Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

Example:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

Given word = "ABCCED", return true.
Given word = "SEE", return true.
Given word = "ABCB", return false.

Notes

Backtrack problem.

  1. when found, mark the point to one.
  2. Use dfs to go down from this point.
  3. Can’t go down anymore, mark the point back to zero (backtrack step).

Solution

Solution 1: Backtrack, dfs

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        for i in range(len(board)):
            for j in range(len(board[0])):
                if self.dfs(board, word, 0, i, j):
                    return True
        return False

    def dfs(self, board, word, w, i, j):
        if w == len(word):
            return True

        if i < 0 or j < 0 or i >= len(board) or j >= len(board[0]):
            return False

        if board[i][j] == word[w]:
            board[i][j] = '-'

            for next_i, next_j in [[i + 1, j], [i - 1, j], [i, j - 1],
                                   [i, j + 1]]:
                if self.dfs(board, word, w + 1, next_i, next_j):
                    return True

            board[i][j] = word[w]

        return False

45 - Jump Game II

leetcode

Problem

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

Example:

Input: [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.
Note:

You can assume that you can always reach the last index.

Notes

Greedy problem.

  1. [begin, end], go through values in between, have one furthest reach.
  2. current index reach to end, jump once.
  3. next interval is [end, reach]

Solution

#+begin_src python class Solution: def jump(self, nums):

jumps = 0 curFurthest = 0 curEnd = 0

for i in range(len(nums) - 1):

curFurthest = max(curFurthest, i + nums[i])

if (i = curEnd): jumps + 1 cur

1306 - Jump Game III

leetcode

Problem

Given an array of non-negative integers arr, you are initially positioned at start index of the array. When you are at index i, you can jump to i + arr[i] or i - arr[i], check if you can reach to any index with value 0.

Notice that you can not jump outside of the array at any time.

Example 1:

Input: arr = [4,2,3,0,3,1,2], start = 5
Output: true
Explanation:
All possible ways to reach at index 3 with value 0 are:
index 5 -> index 4 -> index 1 -> index 3
index 5 -> index 6 -> index 4 -> index 1 -> index 3

Example 2:

Input: arr = [4,2,3,0,3,1,2], start = 0
Output: true
Explanation:
One possible way to reach at index 3 with value 0 is:
index 0 -> index 4 -> index 1 -> index 3

Example 3:

Input: arr = [3,0,2,1,2], start = 2
Output: false
Explanation: There is no way to reach at index 1 with value 0.

Constraints:

1 <= arr.length <= 5 * 10^4
0 <= arr[i] < arr.length
0 <= start < arr.length

Notes

Use dfs to search for 0. Mark the visited place to trigger stop.

Important:

Remember to reset the mark if can not find along the path, so that it can search into another path.

Solution

#+begin_src python class Solution: def canReach(self, arr, start): if start >= len(arr) or start < 0: return False

if arr[start] == 0: return True

if arr[start] == -1: return False

step = arr[start] arr[start] = -1

if self.canReach(arr, start - step) or self.canReach(arr, start + step): return True else:

84 - Largest Rectangle in Histogram

leetcode

Problem

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

Example:

Input: [2,1,5,6,2,3]
Output: 10

Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

The largest rectangle is shown in the shaded area, which has area = 10 unit.

Notes

Main idea is to caculate both left edge and right edge for every entry in the array

Two ways of solution.

Brute Force

Generate two arrays left[] and right[] to keep the two edges of every entry.

  1. one loop to caculate left[].
  2. one loop to caculate right[].
  3. one loop to go through all the edges to caculate the square.

Improvement of the brute force

We can:

store the number of the left bars, which are >= current bar, in left[]

store the number of the right bars, which are >= current bar, in right[]

How to avoid duplicating searching.

  1. left[current] = left[current - 1]
  2. jump left[current - 1] steps to check the next interval of the array

Stack

Create a stack to store the index of the entry.

  1. if current entry is smaller than the top, we find the right edge of the top entry. pop it out and caculate the the max square of the top entry
  2. if current entry is not smaller than the top, push it into stack
  3. go through the left entries in the stack. The lefts ones are all have the longest bar at the top.

Solution

Solution 1: brute-force

class Solution:
    def largestRectangleArea(self, heights):

        if not heights:
            return 0

        n = len(heights)
        res = 0

        left = [ i for i in range(n) ]
        right = [ i for i in range(n) ]

        # caculate for the left edge
        for i in range(n):
            p = i
            while p >= 0:
                if heights[p] < heights[i]:
                    break
                p -= 1
            left[i] = p

        # caculate for the right edge
        for i in range(n):
            p = i
            while p < n:
                if heights[p] < heights[i]:
                    break
                p += 1
            right[i] = p

        for i in range(n):
            res = max(res, heights[i] * (right[i] - left[i] - 1))

        return res

print(Solution().largestRectangleArea([2, 1, 5, 6, 2, 3]))

O(n^2)

It will execeed time limit on leetcode.

10

Solution 2: improved version

class Solution:
    def largestRectangleArea(self, heights):
        if not heights:
            return 0

        res = 0
        n = len(heights)
        left = [1] * n
        right = [1] * n

        # caculate left[]
        for i in range(n):
            p = i - 1
            while p >= 0:
                if heights[p] >= heights[i]:
                    left[i] += left[p]
                    # jump backward
                    p -= left[p]
                else:
                    break

        # caculate right[]
        for i in range(n - 1, -1, -1):
            p = i + 1
            while p < n:
                if heights[p] >= heights[i]:
                    right[i] += right[p]
                    # jump forward
                    p += right[p]
                else:
                    break

        for i in range(n):
            res = max(res, heights[i] * (left[i] + right[i] - 1))

        return res

print(Solution().largestRectangleArea([2, 1, 5, 6, 2, 3]))

Generale Case: O(n), because it uses the jump Worst Case: O(n^2)

Solution 3: stack

class Solution:
    def largestRectangleArea(self, heights):

        stack = []
        n = len(heights)
        res = 0
        index = 0

        while index < n:

            if not stack or heights[stack[-1]] <= heights[index]:
                stack.append(index)
                index += 1
            else:
                top = stack.pop()
                area = (heights[top] *
                        ((index - stack[-1] - 1) if stack else index))

                res = max(res, area)

        while stack:
            h = stack.pop()
            res = max(
                r

85 - Maximal Rectangle

leetcode

Problem

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.

Example:

Input:
[
  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]
]
Output: 6

Notes

Two parts:

  1. generate a heights histogram for every row.
  2. apple “largest rectangle in histogram” on each row of histogram

Solution

#+begin_src python :results output class Solution: def maximalRectangle(self, matrix): if not matrix or not matrix[0]: return 0

m = len(matrix) n = len(matrix[0])

histograms = [[0] * n for i in range(m)]

res = 0

for i in range(m): for j in range(n): if matrix[i][j] == “1”: histograms[i][j] = histograms[i - 1][j] + 1 if i > 0 else 1

for histogram in histograms: res = max(res, self.largestRectangleHistogram(histogram))

return res

def largestRectangleHistogram(self, histogram):

if not histogram: return 0

stack = [] res = 0 index = 0 n = len(histogram)

while index < n: if not stack or histogram[index] >= histogram[stack[-1]]: stack.append(index) index += 1 else: res = max( res, histogram[stack.pop()] * ((index - stack[-1] - 1) if stack else index))

while stack: height = histogram[stack.pop()] res = max(res, height * ((n - stack[-1] - 1) if stack else n))

return res

maxtrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,

121 - Best Time to Buy and Sell Stock

leetcode

Problem

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Note that you cannot sell a stock before you buy one.

Example 1:

Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
             Not 7-1 = 6, as selling price needs to be larger than buying price.

Example 2:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

Notes

Mark the minPrice and the minProfit

Solution

#+begin_src python class Solution: def maxProfit(self, prices): if not prices: return 0

minPrice = prices[0] maxProfit = 0

for price in prices: if price < minPrice: minPrice = price if price - minPrice > maxProfit: maxProfit = price

122 - Best Time to Buy and Sell Stock II

leetcode

Problem

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
             Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
Example 2:

Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
             Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
             engaging multiple transactions at the same time. You must sell before buying again.
Example 3:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

Notes

Solution 1

Find the valley and peak, save the local maxProfit, add it up when new valley is found.

Solution 2

Simplify the solution 1: we can sum up the profit when we go up step by step.

Solution

Solution 1:

class Solution:
    def maxProfit(self, prices):
        if not prices:
            return 0

        valley = prices[0]
        maxProfit = 0
        res = 0

        for price in prices:
            if price - valley < maxProfit:
                valley = price # new valley
                res += maxProfit # add the current maxProfit
                maxProfit = 0 # reset current maxProfit
            else:
                maxProfit = price - valley # update the maxProfit when we still go up

        # remember to add the last maxProfit.
        # 1. When we are on the peak. maxProfit > 0, it should be added
        # 2. When we are in the vally, maxProfit = 0, would not affect the value
        res += maxProfit

        return res

Solution 2:

#+begin_src python class Solution: def maxProfit(self, prices): res = 0 for i in range(1, len(prices)): if (prices[i-1] < prices[i]):

123 - Best Time to Buy and Sell Stock III

leetcode

Problem

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
             Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
Example 2:

Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
             Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
             engaging multiple transactions at the same time. You must sell before buying again.
Example 3:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

Notes

A very good article about how to solve this kind of problem generally. [Reference]​

DP problem have three main sub problems:

  1. states => DP-Table
  2. states transition => Transition
  3. base cases => Padding or initializtion

DP is also a form of brute force method. We have to find all the states of the problem. And we try to use the sub-states to apply the state transitions to simplify the calculation.

How many states do we have in all:

  1. number of days, n
  2. numebr of transactions, k = 2
  3. have the stock or not, 0 or 1

state[n][k][0 or 1] means:

the state at the n-th day, already k times transactions, have or have not the stock in hand.

State Transition:

  1. state[n][k][0] = max(state[n-1][k][0], state[n-1][k][1] + prices)

    state: I have no stock in hand

    It can transit from two states:

    • I do not have stock yesterday => state[n-1][k][0]

    • I do have stock yesterday, and I sell it => state[n-1][k][1] + price

  2. state[n][k][1] = max(state[n-1][k][1], state[n-1][k-1][0] - prices)

    state: I have stock in hand

    It can transit from two states:

    • I do have stock yesterday => state[n-1][k][1]

    • I don’t have stock yesterday, and I buy it => state[n-1][k-1][1] - price

Solution

class Solution:
    def maxProfit(self, prices):
        max_k = 2
        dp = [ [ [0] * 2 for k in range(max_k + 1) ] for i in range(len(prices))]
        for i in range(len(prices)):
            for k in range(1, max_k + 1):
                if i == 0:
                    dp[i][k][0] = 0
                    dp[i][k][1] = -prices[i]
                else:
                    dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
                    dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])

        return dp[-1][max_k][0]

Simple Version: reduce the DP-Table

We only have to maintain two 1-dimentional states.

import sys
class Solution:
    def maxProfit(self, prices):
        buy = [-sys.maxsize] * 3
        sell = [0] * 3
        for price in prices:
            buy[1] = max(buy[1], sell[0]-price)
            buy[2] = max(buy[2], sell[1]-price)
            sell[1] = max(sell[1], buy[1]+price)
            sell[2] = max(sell[2], buy[2]+price)
            print(buy)
            print(sell)
        return sell[2]

print(Solution().maxProfit([3,3,5,0,0,3,1,4]))

#+begin_example [-9223372036854775807, -3, -3] [0, 0, 0] [-9223372036854775807, -3, -3] [0, 0, 0] [-9223372036854775807, -3, -3] [0, 2, 2] [-9223372036854775807, 0, 2] [0, 2, 2] [-9223372036854775807, 0, 2] [0, 2, 2] [-

188 - Best Time to Buy and Sell Stock IV

leetcode

Problem

Say you have an array for which the i-th element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Example 1:

Input: [2,4,1], k = 2
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.

Example 2:

Input: [3,2,6,5,0,3], k = 2
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4.
             Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.

Notes

See problem 121, 122 and 123.

Note the k. If it is too large(>= n/2), treat it as if with infinitive transactions. We don’t want to loop through a too large k.

Solution

#+begin_src python class Solution: def maxProfit(self, k, prices): if not prices: return 0

n = len(prices) if k >= n//2: # treat it as infinitive transactions res = 0 for i in range(1, n): if prices[i] > prices[i-1]: res += prices[i] - prices[i-1] return res

buy = [-sys.maxsize] * (k+1) sell = [0] * (k+1)

for price in prices: for i in range(1, k+1): buy[i] = max(buy[i], sell[i-1]-price) sell[i] = max(sel

309 - Best Time to Buy and Sell Stock with Cooldown

leetcode

Problem

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example:

Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]

Notes

Solution

#+begin_src python import sys class Solution: def maxProfit(self, prices): dp_buy = -sys.maxsize dp_sell = 0 dp_pre_0 = 0

for price in prices: tmp = dp_sell dp_sell = max(dp_sell, dp_buy + price) dp_buy = max(dp_buy, dp_pre_0 - price) dp_pre_0 = tmp

104 - Maximum Depth of Binary Tree

leetcode

Problem

Given a binary tree, find its maximum depth.

The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Note: A leaf is a node with no children.

Example:

Given binary tree [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

Notes

Recursion is your friend!

Solution

#+begin_src python class Solution: def maxDepth(self, root): return 1 + max(self.maxDepth(root.right), self.maxDepth(

21 - Merge Two Sorted Lists

leetcode

Problem

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

Example:

Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4

Notes

Recursion is your friend!

Solution

Solution 1: Recursive

class Solution:
    def mergeTwoLists(self, l1, l2):
        if not l1:
            return l2
        if not l2:
            return l1

        if l1.val > l2.val:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2
        else:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1

101 - Symmetric Tree

leetcode

Problem

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).

For example, this binary tree [1,2,2,3,4,4,3] is symmetric:

    1
   / \
  2   2
 / \ / \
3  4 4  3


But the following [1,2,2,null,3,null,3] is not:

    1
   / \
  2   2
   \   \
   3    3

Notes

Recursion !

Solution

Solution 1: recursive

#+begin_src python class Solution: def isSymmetric(self, root): if not root: return True return self.isMirrored(root.left, root.right)

def isMirrored(self, left, right):

      if not left and not right:
          return True
      elif not left:
          return False
      elif not right:
          return False
      else:
          if left.val == right.val:
              return self.isMirrored(left.left, right.right) and \\
                  self.isMirrored(left.right, right.left)
          else:
              return False

#+end_sr

198 - House Robber

leetcode

Problem

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
             Total amount you can rob = 1 + 3 = 4.
Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
             Total amount you can rob = 2 + 9 + 1 = 12.

Notes

States: 1. house, 2. rob or not rob -> dp[i][0 or 1]

Transition: dp[i][0] = max(dp[i-1][0], dp[i-1][1]), dp[i][1] = dp[i-1][0] + nums[i]

Base Case: dp[0][0] = 0, dp[0][1] = 0

Solution

class Solution:
    def rob(self, nums):
        robbed, notRobbed = 0, 0
        for i in nums:
            robbed, notRobbed = notRobbed + i, max(robbed, notRobbed)

300 - Longest Increasing Subsequence

leetcode

Problem

Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Note:

There may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?

Solution

DP problem.

States: index, dp[index] = longest increasing subsequence at this position

Transition: if nums[j] < nums[i]: dp[i] = max(dp[i], dp[j] + 1)

Base Case: dp[0] = 1

Solution 1: DP with O(n^2)

class Solution:
    def lengthOfLIS(self, nums):
        dp = [1] * len(nums)

        for i in range(1, len(nums)):
            for j in range(i):
               if nums[j] < nums[i]:
                   dp[i] = max(dp[i], dp[j] + 1)

       return max(dp) if nums else 0

Solution 2: DP with binary search for LIS

class Solution:
    def lengthOfLIS(self, nums):
        if not nums:
            return 0
        dp = [nums[0]]
        for i in range(1, len(nums)):
            if nums[i] > dp[-1]:
                dp.append(nums[i])
            else:
                j = self.binarySearch(dp, nums[i])
                dp[j] = nums[i]

        return len(dp)

    def binarySearch(self, nums, target):
        l = 0
        r = len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] > target:
                r = mid - 1
            elif nums[mid] < target:
                l = mid + 1
            else:
                return mid

        return l

Time: O(nlogn)

Space: O(n)

322 - Coin Change

leetcode

Problem

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
Example 2:

Input: coins = [2], amount = 3
Output: -1

Note:
You may assume that you have an infinite number of each kind of coin.

Notes

DP problem

States: amount

Transition: dp[i] = min(dp[i], dp[i-coins[j]]+1)

Solution

class Solution:
    def coinChange(self, coins, amount):
        dp = [float('inf')] * (amount + 1)

        dp[0] = 0

        for i in range(1, amount + 1):
            for j in range(len(coins)):
                if i - coins[j] >= 0:
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1)

        return dp[-1] if dp[-1] != float('inf') else -1

152 - Maximum Product Subarray

leetcode

Problem

Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.

Example 1:

Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.
Example 2:

Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.

Notes

DP problem:

Solution

Solution 1: DP

Original Version:

class Solution:
    def maxProduct(self, nums):
        if not nums:
            return 0

        dp = [[0] * 2 for i in range(len(nums))]
        res = dp[0][0] = dp[0][1] = nums[0]

        for i in range(1, len(nums)):
            if nums[i] >= 0:
                dp[i][0] = max(nums[i], dp[i - 1][0] * nums[i])
                dp[i][1] = dp[i - 1][1] * nums[i]
            else:
                dp[i][0] = dp[i - 1][1] * nums[i]
                dp[i][1] = min(nums[i], nums[i] * dp[i - 1][0])
            res = max(res, dp[i][0])
        return res

Simplify Version 1:

class Solution:
    def maxProduct(self, nums):
        if not nums:
            return 0

        pos = neg = res = nums[0]
        for i in nums[1:]:
            if i>=0:
                pos = max(i, pos*i)
                neg = neg*i
            else:
                tmp = pos # Important: remember the value, because we are gonna alter it.
                pos = neg*i
                neg = min(tmp*i, i) # Use the original value
                # We also can write as this, but we should notice it only works in like python or ruby
                # pos, neg = neg*i, min(pos*i, i), simplify to version 3
            res = max(pos, res)
        return res

Simplify Version 2:

class Solution:
    def maxProduct(self, nums):
        if not nums:
            return 0

        pos = neg = res = nums[0]
        for i in nums[1:]:
            if i>=0:
                pos, neg = max(i, pos*i), neg*i
            else:
                pos, neg = neg*i, min(i, pos*i)
            res = max(pos, res)
        return res

Solution 2: Prefix sum and Suffix sum

class Solution:
    def maxProduct(self, nums):
        rnums = nums[::-1]
        for i in range(1, len(nums)):
            nums[i] *= nums[i-1] or 1
            rnums[i] *= rnums[i-1] or 1
        return max(nums+rnums)

Time: O(n) Space: O(n)

96 - Unique Binary Search Trees

leetcode

Problem

Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n?

Example:

Input: 3
Output: 5
Explanation:
Given n = 3, there are a total of 5 unique BST's:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

Notes

Solution 1: DP

In this dp problem, the hard part is to figure out the transition.

Solution 2: Catalan Number

Catalan Number

Cn = 1/(n+1)(2n n) = (2n)!/(n+1)!n!

The first Catalan numbers for n = 0, 1, 2, 3, ... are

1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012,
742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190,
6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452

Solution

Solution 1: DP

class Solution:
    def numTrees(self, n):
        if n < 1:
            return 0

        dp = [0] * (n+1)
        dp[0] = 1

        for i in range(1, n+1):
            for j in range(0, i):
                dp[i] += dp[j]*dp[i-1-j]

        return dp[n]

Solutiojn 2: Catalan Number

import math
class Solution:
    def numTrees(self, n):
        return math.factorial(2*n)/(math.factorial(n)*math.factorial(n+1))

221 - Maximal Square

leetcode

Problem

Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.

Example:

Input:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Output: 4

Notes

DP Problem:

Solution

Original Version :

class Solution:
    def maximalSquare(self, matrix):
        if not matrix:
            return 0

        dp = [[[0] * 3 for j in range(len(matrix[0]) + 1)] for i in range(len(matrix) + 1)]
        res = 0
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                if matrix[i][j] == "1":
                    dp[i + 1][j + 1][0] = dp[i][j + 1][0] + 1
                    dp[i + 1][j + 1][1] = dp[i + 1][j][1] + 1
                    dp[i + 1][j + 1][2] = min(dp[i][j][2]+1, dp[i + 1][j + 1][0], dp[i + 1][j + 1][1])
                    res = max(res, dp[i + 1][j + 1][2])

        return res*res

Time: O(mn) Space: O(mn)

Simplified Version 1: optimize the space, 3xn space :

class Solution:
    def maximalSquare(self, matrix):
        if not matrix:
            return 0

        dp = [[0] * 3 for j in range(len(matrix[0]) + 1)]
        res = 0

        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                if matrix[i][j] == "1":
                    dp[j+1][0] = dp[j+1][0] + 1
                    dp[j+1][1] = dp[j][1] + 1
                    dp[j+1][2] = min(dp[j+1][2]+1, dp[j+1][0]+1, dp[j][1]+1)
                    res = max(res, dp[j + 1][2])
                else:
                    dp[j+1][0] = dp[j+1][1] = dp[j+1][2] = 0

        return res*res

Space: O(n)

Simplified Version 2: n space :

class Solution:
    def maximalSquare(self, matrix):
        if not matrix:
            return 0

        dp = [0 for j in range(len(matrix[0]) + 1)]
        res = 0
        prev = 0 # previous diagnol element

        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                temp = dp[j+1]
                if matrix[i][j] == "1":
                    dp[j+1] = min(prev, dp[j+1], dp[j]) + 1
                    res = max(res, dp[j + 1])
                else:
                    dp[j+1] = 0

                prev = temp

        return res*res

Simplified Version 3: in place :

class Solution:
    def maximalSquare(self, matrix):
        if not matrix:
            return 0
        res = 0
        m = len(matrix)
        n = len(matrix[0])

        for i in range(m):
            for j in range(n):
                if matrix[i][j] == "1":
                    if i == 0 or j == 0:
                        matrix[i][j] = 1
                    else:
                        matrix[i][j] = min(matrix[i - 1][j - 1],
                                           matrix[i][j - 1],
                                           matrix[i - 1][j]) + 1
                    res = max(res, matrix[i][j])
                else:
                    matrix[i][j] = 0

        return res * res

Space: O(1)

279 - Perfect Squares

leetcode

Problem

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.

Example 1:

Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.
Example 2:

Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.

Notes

DP problem

Solution

import math
class Solution:
    dp = [0]
    def numSquares(self, n):
        _dp = self.dp
        if len(_dp) >= n + 1:
            return _dp[n]
        else:
            for i in range(len(_dp), n+1):
                _dp += [min([_dp[i-j*j]+1 for j in range(1, int(math.sqrt(i))+1) if i-j*j >= 0])]
        return _dp[n]

print(Solution().numSquares(12))

647 - Palindromic Substrings

leetcode

Problem


Given a string, your task is to count how many palindromic substrings in this string.

The substrings with different start indexes or end indexes are counted as different substrings even they consist of same characters.

Example 1:

Input: "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".


Example 2:

Input: "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

Note:

The input string length won't exceed 1000.

Notes

DP problem

Solution

class Solution:
    def countSubstrings(self, s):
        dp = [[0] * len(s) for i in range(len(s))]
        res = 0
        for r in range(len(s)):
            for l in range(r+1):
                if s[r] == s[l]:
                    if r == l or r+1 == l or dp[l+1][r-1] == 1:
                        dp[l][r] = 1
                        res += 1
        return res
print(Solution().countSubstrings("aba"))

5 - Longest Palindromic Substring

leetcode

Problem

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:

Input: "cbbd"
Output: "bb"

Notes

DP problem

Solution

Solution 1: DP

class Solution:
    def longestPalindrome(self, s: str) -> str:
        dp = [[0] * len(s) for i in range(len(s))]
        res = ""
        for r in range(len(s)):
            for l in range(r + 1):
                if s[l] == s[r]:
                    if l == r or l + 1 == r or dp[l + 1][r - 1] == 1:
                        dp[l][r] = 1
                        if r - l + 1 > len(res):
                            res = s[l:r + 1]
        return res

Solution 2: Central Expansion

136 - Single Number

leetcode

Problem

Given a non-empty array of integers, every element appears twice except for one. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:

Input: [2,2,1]
Output: 1
Example 2:

Input: [4,1,2,1,2]
Output: 4

Notes

Solution 1: Hash Table

Solution 2: Bit manipulation

Consider XOR all element together. The left number is the single number.

Solution

Solution 1: Hash Table

class Solution:
    def singleNumber(self, nums):
        table = {}
        for i in nums:
            table[i] = table.get(i, 0) + 1
        for k in table:
            if table[k] == 1:
                return k

Solution 2: Bit manipulation for finding the single number

class Solution:
    def singleNumber(self, nums):

        res = 0

        for i in nums:
            res ^= i

        return res

print(Solution().singleNumber([2, 2, 5, 1, 8, 1, 9, 8, 9]))

Solution 3: Python reduce

from functools import reduce
import operator
nums = [2, 2, 5, 1, 8, 1, 9, 8, 9]
print(reduce(lambda x, y: x ^ y, nums))
print(reduce(operator.xor, nums))

202 - Happy Number

leetcode

Problem

Write an algorithm to determine if a number is "happy".

A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.

Example:

Input: 19
Output: true
Explanation:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

Notes

Hash Table

Solution

class Solution:
    def isHappy(self, n):
        table = {}

        while n != 1:
            x = n
            n = 0
            while x != 0:
                n += (x % 10)**2
                x //= 10

            if table.get(n):
                return False
            else:
                table[n] = 1
        return True


class Solution:
    def isHappy(self, n):
        mem = set()
        while n not in mem:
            mem.add(n)
            n = sum(int(i)**2 for i in str(n))
        return n == 1

338 - Counting Bits

leetcode

Problem

Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array.

Example 1:

Input: 2
Output: [0,1,1]
Example 2:

Input: 5
Output: [0,1,1,2,1,2]

Notes

DP problem

dp[0] = 0
dp[1] = dp[1-1] + 1 = 1
dp[2] = dp[2-2] + 1 = 1
dp[3] = dp[3-2] + 1 = 2
dp[4] = dp[4-4] + 1 = 1
dp[5] = dp[5-4] + 1 = 2
dp[6] = dp[6-4] + 1 = 2
dp[7] = dp[7-4] + 1 = 3
dp[8] = dp[8-8] + 1 = 1
dp[9] = dp[9-8] + 1 = 2
dp[i] = dp[i - log_2 (i)] + 1

A trick using bit manipulation:

8 -> 1000
9 -> 1001
10 -> 1010

9 & 8 -> 1001 & 1000 -> 1000 dp[8] + 1  = 2
10 & 9 -> 1010 & 1001 -> 1000 dp[8] + 1 = 2
11 & 10 -> 1011 & 1010 -> 1010 dp[10] + 1 = 3
12 & 11 -> 1100 & 1011 -> 1000 dp[8] + 1 = 2

dp[i] = dp[i & (i-1)] + 1

Solution

Solution 1: DP

class Solution:
    def countBits(self, num):
        offset = 1
        dp = [0] * (num + 1)
        for i in range(1, num + 1):
            if offset * 2 == i:
                offset *= 2
            dp[i] = dp[i - offset] + 1
        return dp

Solution 2: Bit manipulation on couting bits

class Solution:
    def countBits(self, num):
        dp = [0] * (num + 1)
        for i in range(1, num + 1):
            dp[i] = dp[i & (i - 1)] + 1
        return dp

Solution 3:

class Solution(object):
    def countBits(self, num):
        res=[0]
        while len(res)<=num:
            res+=[i+1 for i in res]
        return res[:num+1]

Solution 4:

class Solution:
    def countBits(self, num):
        dp = [0] * (num + 1)
        for i in range(1, num+1):
            if not i % 2:
                dp[i] = dp[i >> 1]
            else:
                dp[i] = dp[i >> 1] + 1

        return dp[num]

494 - Target Sum

leetcode

Problem

You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.

Find out how many ways to assign symbols to make sum of integers equal to target S.

Example 1:
Input: nums is [1, 1, 1, 1, 1], S is 3.
Output: 5
Explanation:

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

There are 5 ways to assign symbols to make the sum of nums be target 3.
Note:
The length of the given array is positive and will not exceed 20.
The sum of elements in the given array will not exceed 1000.
Your output answer is guaranteed to be fitted in a 32-bit integer.

Solution

DP problem. The hardest part of this problem is to find the states.

What states do we have to calculate the current result, which is the number of ways of reaching the target.

We need all the sum values of the previous number to calculate the current one.

for j in range(2sum+1):
    if dp[i-1][j] > 0: # only check the ones, which have been reached
      if j+nums[i] < 2sum + 1: # sign + to the current number, we transit from j to j+nums[i]
        dp[i][j+nums[i]] += dp[i-1][j]
      if j-nums[i] >= 0: # sign + to the current number, we transit from j to j-nums[i]
        dp[i][j-nums[i]] += dp[i-1][j]

We start from the offset.

# the first number with symbols
dp[0][offset+nums[0]] +=1
dp[0][offset-nums[0]] +=1

Important: You have to use += 1, not only = 1, because the number can be 0.

For exmaple: we take sum = 5 When the first number is 1 (not 0):

d[0][0+sum + 1] += 1 -> dp[0][6] = 1
d[0][0+sum - 1] += 1 -> dp[0][4] = 1

It means:

there is one ways to reach 1 (6 - offset)

there is one ways to reach -1 (4 - offset)

When the first number is 0:

d[0][0+sum + 1] += 1 -> dp[0][5] = 1
d[0][0+sum - 1] += 1 -> dp[0][5] = 2

It means there are 2 ways to reach 0 (5 - offset)

Solution 1: 2D - DP

class Solution():
    def findTargetSumWays(self, nums, S):
        l = len(nums)
        n = 2 * sum(nums) + 1
        offset = sum(nums)

        dp = [[0] * n for i in nums]

        if not nums:
            return 0

        # base case
        dp[0][offset - nums[0]] += 1
        dp[0][offset + nums[0]] += 1

        for i in range(1, len(nums)):
            for j in range(n):
                if dp[i-1][j] > 0:
                    if j + nums[i] < n:
                        dp[i][j + nums[i]] += dp[i-1][j]
                    if j - nums[i] >= 0:
                        dp[i][j - nums[i]] += dp[i-1][j]

        return dp[-1][offset + S]


print(Solution().findTargetSumWays([1, 1, 1, 1, 1], 3))

Time: O(l*n) Space: O(l*n)

Solution 2: 1D - DP

We notice that, to calculate the dp array at the current index, we only have to know one previous row.

So we only have to maintain two dp arrays to record previous values and current values respectly.

class Solution:
    def findTargetSumWays(self, nums, S):
        if not nums:
            return 0
        l = len(nums)
        n = 2 * sum(nums) + 1
        offset = sum(nums)
        dp = [[0] * n for i in nums]

        dp[0][offset + nums[0]] += 1
        dp[0][offset - nums[0]] += 1

        for i in range(1, l):
            for j in range(n):
                if dp[i - 1][j] > 0:
                    if dp[i - 1][j] < n:
                        dp[i][j + nums[i]] += dp[i - 1][j]
                    if dp[i - 1][j] >= 0:
                        dp[i][j - nums[i]] += dp[i - 1][j]

        return dp[-1][offset + S]

print(Solution().findTargetSumWays([1, 1, 1, 1, 1], 3))

283 - Move Zeros

leetcode

Problem

Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.

Example:

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
Note:

You must do this in-place without making a copy of the array.
Minimize the total number of operations.

Solution

Two pointers.

One pointer is for the start of 0.

One pointer keeps going forward.

class Solution:
    def moveZeros(self, nums):

        p1, p2 = 0, 0

        for p2 < len(nums):
            if nums[p1] != 0:
                p1 += 1
            elif nums[p2] != 0:
                nums[p1], nums[p2] = nums[p2], nums[p1]
                p1 += 1
            p2 += 1

        return nums

416 - Partition Equal Subset Sum

leetcode

Problem

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

Each of the array element will not exceed 100.
The array size will not exceed 200.


Example 1:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].


Example 2:

Input: [1, 2, 3, 5]

Output: false

Explanation: The array cannot be partitioned into equal sum subsets.

Solution

DP problem, it is similar to problem 494 Target Sum.

Maintain a dp table: dp[i][sum]. Don’t forget to the offset.

Transition: dp[i][j-nums[i]] += dp[i-1][j] dp[i][j+nums[i]] += dp[i-1][j]

When the transition is only depend on the last row, usually you always can transform the dp table to 1 dimensional.

Solution 1: 1D-DP

class Solution:
    def canPartition(slef, nums):
        nums_sum = sum(nums)
        if nums_sum % 2 != 0:
            return False

        target = nums_sum//2
        n = target + 1

        dp = [False] * n
        dp[0] = True
        for i in range(len(nums)):
            for j in reversed(range(nums[i], n)):
                dp[j] = dp[j-nums[i]] or dp[j]
                if dp[target]:
                    return True
        return dp[target]

Time: O(len(nums)*sum(nums))

Space: O(len(nums)*sum(nums))

Solution 2: Bit manipulation for maintaning the dp table

class Solution:
    def canPartition(self, nums):
        s = sum(nums)
        if s%2 != 0:
            return False

        bits = 1
        for i in nums:
            bits |= bits << i

        return (bits >> (s//2)) & 1 == 1

Time: O(n)

Space: O(1) maybe, depends on the sum of the array, the bits can be longer

698 - Partition to K Equal Sum Subsets

leetcode

Problem

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal.



Example 1:

Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
Output: True
Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.


Note:

1 <= k <= len(nums) <= 16.
0 < nums[i] < 10000.

Solution


215 - Kth Largest Element in an Array

leetcode

Problem

215. Kth Largest Element in an Array
Medium

3152

222

Add to List

Share
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

Example 1:

Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:

Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.

Solution

The naive solution would be to maintain an array of length k and update the array in each iteration to keep the first kth largest element in the array

Solution 1: Array of k

class Solution:
    def findKthLargest(self, nums, k):
        l = []

        for i in nums:
            if not l:
                l.append(i)
            else:
                len0 = len(l)
                for j in range(len(l)):
                    if i >= l[j]:
                        l = l[0:j] + [i] + l[j:]
                        break
                if len0 == len(l) and len0 != k:
                    l.append(i)
                l = l[0:k]
        return l[-1]

Time: O(nk)

Space: O(k)

Solution 2: Partition to find the kth largest

class Solution:
    def findKthLargest(self, nums, k):
        pos = self.partition(nums, 0, len(nums) - 1)
        if pos + 1 < k:
            return self.findKthLargest(nums[pos + 1:], k - pos - 1)
        elif pos + 1 > k:
            return self.findKthLargest(nums[0:pos], k)
        else:
            return nums[pos]

    def partition(self, nums, l, r):
        p = r

        while(l < r):
            while l < r and nums[l] > nums[p]:
                l += 1
            while l < r and nums[r] <= nums[p]:
                r -= 1
            nums[l], nums[r] = nums[r], nums[l]
        nums[l], nums[p] = nums[p], nums[l]
        return l

Time: O(klogn)

Space: O(1)

49 - Group Anagrams

leetcode

Problem

Given an array of strings, group anagrams together.

Example:

Input: ["eat", "tea", "tan", "ate", "nat", "bat"],
Output:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]
Note:

All inputs will be in lowercase.
The order of your output does not matter.

Solution

Use the sorted string as key in hash table

import collections
class Solution:
    def groupAnagrams(self, strs):
        ans = collections.defaultdict(list)
        for s in strs:
            ans[tuple(sorted(s))].append(s)
        return list(ans.values())

Or use the counter

import collections
class Solution:
    def groupAnagrams(self, strs):
        ans = collections.defaultdict(list)
        for s in strs:
            a = [0] * 26
            for c in s:
                a[ord(c) - ord('a')] += 1
            ans[tuple(a)].append(s)
        return list(ans.values())

876 - Middle of the Linked List

leetcode

Problem

Given a non-empty, singly linked list with head node head, return a middle node of linked list.

If there are two middle nodes, return the second middle node.



Example 1:

Input: [1,2,3,4,5]
Output: Node 3 from this list (Serialization: [3,4,5])
The returned node has value 3.  (The judge's serialization of this node is [3,4,5]).
Note that we returned a ListNode object ans, such that:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL.
Example 2:

Input: [1,2,3,4,5,6]
Output: Node 4 from this list (Serialization: [4,5,6])
Since the list has two middle nodes with values 3 and 4, we return the second one.

Solution

Solution 1: two-pointer

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        p1 = p2 = head
        while p2 and p2.next:
            p2 = p2.next.next
            p1 = p1.next

        return p1

Solution 2: counter

class Solution:
    def middleNode(self, head):
        mid = head
        count = 0
        while head:
            count += 1
            if count % 2 == 0:
                mid = mid.next
            head = head.next
        return mid

844 - Backspace String Compare

leetcode

Problem

Given two strings S and T, return if they are equal when both are typed into empty text editors. # means a backspace character.

Example 1:

Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".
Example 2:

Input: S = "ab##", T = "c#d#"
Output: true
Explanation: Both S and T become "".
Example 3:

Input: S = "a##c", T = "#a#c"
Output: true
Explanation: Both S and T become "c".
Example 4:

Input: S = "a#c", T = "b"
Output: false
Explanation: S becomes "c" while T becomes "b".
Note:

1 <= S.length <= 200
1 <= T.length <= 200
S and T only contain lowercase letters and '#' characters.

Solution

Solution 1: stack

class Solution:
    def backspaceCompare(self, S, T):
        return self.build(S) == self.build(T)

    def build(self, s):
        res = []

        for c in s:
            if c != "#":
                res.append(c)
            elif res:
                res.pop()

        return "".join(res)

print(Solution().backspaceCompare("ac#cc#", "ab#c"))
from functools import reduce

class Solution:
    def backspaceCompare(self, S, T):
        def back(res, c):
            if c != "#":
                res.append(c)
            elif res:
                res.pop()
            return res

        return reduce(back, S, []) == reduce(back, T, [])

print(Solution().backspaceCompare("ac#cc#", "ab#c"))

Solution 2: two pointer, reserve

class Solution:
    def backspaceCompare(self, S, T):
        pS, pT = len(S) - 1, len(T) - 1
        backS, backT = 0, 0

        while pS >= 0 or pT >= 0:
            while pS >= 0:
                if S[pS] == "#":
                    backS += 1
                    pS -= 1
                elif backS:
                    backS -= 1
                    pS -= 1
                else:
                    break

            while pT >= 0:
                if T[pT] == "#":
                    backT += 1
                    pT -= 1
                elif backT:
                    backT -= 1
                    pT -= 1
                else:
                    break

            if not (pS >= 0 and pT >= 0 and S[pS] == T[pT]):
                return pS == pT == -1

            pS -= 1
            pT -= 1

        return True
print(Solution().backspaceCompare("###ac#b", "ab#b"))

155 - Min Stack

leetcode

Problem

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
getMin() -- Retrieve the minimum element in the stack.


Example:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> Returns -3.
minStack.pop();
minStack.top();      --> Returns 0.
minStack.getMin();   --> Returns -2.

Solution

Use a second stack to keep track of the min value. Pay attention to the return value. Remember to return None when there is no result.

class MinStack:
    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.min = []

    def push(self, x: int) -> None:
        self.stack.append(x)
        if not self.getMin() or self.getMin() >= x:
            self.min.append(x)

    def pop(self) -> None:
        if self.stack:
            x = self.stack.pop()
            if x == self.min[-1]:
                self.min.pop()

    def top(self) -> int:
        if self.stack:
            return self.stack[-1]
        else:
            return None

    def getMin(self) -> int:
        if self.min:
            return self.min[-1]
        else:
            return None

543 - Diameter of Binary Tree

leetcode

Problem

Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

Example:
Given a binary tree
          1
         / \
        2   3
       / \
      4   5
Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].

Note: The length of path between two nodes is represented by the number of edges between them.

Solution

class Solution:
    def diameterOfBinaryTree(self, root: TreeNode) -> int:
        if not root:
            return 0
        elif not root.right and not root.left:
            return 0
        elif not root.right:
            return max(self.diameterOfBinaryTree(root.left),
                       1 + self.height(root.left))
        elif not root.left:
            return max(self.diameterOfBinaryTree(root.right),
                       1 + self.height(root.right))
        else:
            return max(self.diameterOfBinaryTree(root.right),
                       self.diameterOfBinaryTree(root.left),
                       self.height(root.left) + self.height(root.right) + 2)

    def height(self, root):

        if not root:
            return 0
        if not root.right and not root.left:
            return 0

        return 1 + max(self.height(root.left), self.height(root.right))
class Solution:
    def diameterOfBinaryTree(self, root):
        self.ans = 0

        def depth(node):
            if not node:
                return 0
            r = depth(node.right)
            l = depth(node.left)
            self.ans = max(self.ans, r+l)
            return 1 + max(r, l)
        depth(root)
        return self.ans

1046 - Last Stone Weight

leetcode

Problem

We have a collection of stones, each stone has a positive integer weight.

Each turn, we choose the two heaviest stones and smash them together.  Suppose the stones have weights x and y with x <= y.  The result of this smash is:

If x == y, both stones are totally destroyed;
If x != y, the stone of weight x is totally destroyed, and the stone of weight y has new weight y-x.
At the end, there is at most 1 stone left.  Return the weight of this stone (or 0 if there are no stones left.)



Example 1:

Input: [2,7,4,1,8,1]
Output: 1
Explanation:
We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then,
we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then,
we combine 2 and 1 to get 1 so the array converts to [1,1,1] then,
we combine 1 and 1 to get 0 so the array converts to [1] then that's the value of last stone.

Solution

Priority queue, max heap can be used here.

class Solution:
    def lastStoneWeight(self, stones):
        res = [ -s for s in stones ]

        heapq.heapify(res)

        while len(res) > 1 :
            y = heapq.heappop(res)
            x = heapq.heappop(res)
            if y != x:
                heapq.heappush(res, y-x)

        return res[0]*-1 if res else 0

525 - Contiguous Array

leetcode

Problem

Given a binary array, find the maximum length of a contiguous subarray with equal number of 0 and 1.

Example 1:
Input: [0,1]
Output: 2
Explanation: [0, 1] is the longest contiguous subarray with equal number of 0 and 1.
Example 2:
Input: [0,1,0]
Output: 2
Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1.
Note: The length of the given binary array will not exceed 50,000.

Solution

We keep a count, which decrease 1 if 0, increase 1 if 1.

If at x and y, they have the same value v of count, then the numbers between x+1 and y have the equal number of 1s and 0s. The max length = y - (x + 1) + 1 = y - x

And after that, if we found at index w, it also has value v of count. The max length should be w - x.

So we have to maintain a hashmap h using the values of count as keys, and the first index of that count as its value.

The maxlen = max(maxlen, h[count]) if current count in the map.

Important

We have an initial value of the map.

h[0] = -1 (If count == 0 at index i, we have all the numbers between h[0] + 1 and i. And h[0] + 1 should at index 0, so h[0] = -1)

Consider the input [0, 1]:

Index 0: h[-1] = 0, maxlen = 0
Index 1: h[0] = -1, maxlen = 1 - (-1) = 2
maxlen = 2

Solution: counting contiguous 1s and 0s with a Hashmap

class Solution:
    def findMaxLength(self, nums):
        h = {0: -1}

        count = ans = 0
        for i in range(len(nums)):
            count += 1 if nums[0] else -1
            ans = max(ans, h.setdefault(count, i))

        return ans

Perform String Shifts

leetcode

Problem

You are given a string s containing lowercase English letters, and a matrix shift, where shift[i] = [direction, amount]:

direction can be 0 (for left shift) or 1 (for right shift).
amount is the amount by which string s is to be shifted.
A left shift by 1 means remove the first character of s and append it to the end.
Similarly, a right shift by 1 means remove the last character of s and add it to the beginning.
Return the final string after all operations.

Example 1:

Input: s = "abc", shift = [[0,1],[1,2]]
Output: "cab"
Explanation:
[0,1] means shift to left by 1. "abc" -> "bca"
[1,2] means shift to right by 2. "bca" -> "cab"

Example 2:

Input: s = "abcdefg", shift = [[1,1],[1,1],[0,2],[1,3]]
Output: "efgabcd"
Explanation:
[1,1] means shift to right by 1. "abcdefg" -> "gabcdef"
[1,1] means shift to right by 1. "gabcdef" -> "fgabcde"
[0,2] means shift to left by 2. "fgabcde" -> "abcdefg"
[1,3] means shift to right by 3. "abcdefg" -> "efgabcd"

Solution

class Solution:
    def stringShift(self, s: str, shift: List[List[int]]) -> str:
        count = sum([ (-1+2*x)*y for x, y in shift ]) % len(s)
        ds = s + s
        return ds[len(s)-count: 2*len(s) - count]

238 - Product of Array Except Self

leetcode

Problem

238. Product of Array Except Self
Medium

4145

358

Add to List

Share
Given an array nums of n integers where n > 1,  return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].

Example:

Input:  [1,2,3,4]
Output: [24,12,8,6]
Constraint: It's guaranteed that the product of the elements of any prefix or suffix of the array (including the whole array) fits in a 32 bit integer.

Note: Please solve it without division and in O(n).

Follow up:
Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)

Solution

Solution 1: prefix product and suffix product

class Solution:
    def productExceptSelf(self, nums):
        n = len(nums)
        ans = [0] * n
        ans[0] = 1

        for i in range(1, n):
            ans[i] = ans[i-1] * nums[i-1]

        R = 1
        for i in reversed(range(n)):
            ans[i] = ans[i] * R
            R *= nums[i]

        return ans

print(Solution().productExceptSelf([1, 2, 3, 4]))

678 - Valid Parenthesis String

leetcode

Problem

Given a string containing only three types of characters: '(', ')' and '*', write a function to check whether this string is valid. We define the validity of a string by these rules:

Any left parenthesis '(' must have a corresponding right parenthesis ')'.
Any right parenthesis ')' must have a corresponding left parenthesis '('.
Left parenthesis '(' must go before the corresponding right parenthesis ')'.
'*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string.
An empty string is also valid.
Example 1:
Input: "()"
Output: True
Example 2:
Input: "(*)"
Output: True
Example 3:
Input: "(*))"
Output: True
Note:
The string size will be in the range [1, 100].

Solution

Solution 1: cmin and cmax to valida to parenthesis

class Solution:
    def checkValidString(self, s: str) -> bool:
        counter_max = 0
        counter_min = 0

        for c in s:
            if c == "(":
                counter_max += 1
                counter_min += 1
            elif c == ")":
                counter_max -= 1
                counter_min = max(counter_min-1, 0)
            else:
                counter_max += 1
                counter_min = max(counter_min-1, 0)

            if counter_max < 0:
                return False

        return counter_min == 0

200 - Number of Islands

leetcode

Problem

Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

Input:
11110
11010
11000
00000

Output: 1
Example 2:

Input:
11000
11000
00100
00011

Output: 3

Solution

DFS problem. Straight forward.

class Solution:
    def numIslands(self, grid):
        if not grid:
            return 0
        h = len(grid)
        w = len(grid[0])
        ans = 0
        m = [ [0] * w for x in range(h) ]
        for i in range(h):
            for j in range(w):
                if grid[i][j] == "1" and m[i][j] == 0:
                    self.dfs(grid, i, j, m)
                    ans += 1
        return ans

    def dfs(self, grid, i, j, m):
        if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] == "0" or m[i][j] == 1:
            return None
        else:
            m[i][j] = 1
            self.dfs(grid, i, j+1, m)
            self.dfs(grid, i, j-1, m)
            self.dfs(grid, i-1, j, m)
            self.dfs(grid, i+1, j, m)
        return None

46 - Permutations

leetcode

Problem

Given a collection of distinct integers, return all possible permutations.

Example:

Input: [1,2,3]
Output:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

Solution

Solution 1

class Solution:
    def permute(self, nums):
        ans = []
        self.backtrack([], nums, ans)
        return ans

    def backtrack(self, path, nums, ans):
        if not nums:
            ans.append(path[:])
            return
        for i, v in enumerate(nums):
            self.backtrack(path+[v], nums[:i]+nums[i+1:], ans)

Solution 2

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        ans = []
        marked = [False] * len(nums)
        self.dfs(nums, [], marked, ans)
        return ans

    def dfs(self, nums, path, marked, ans):
        if len(path) == len(nums):
            ans.append(list(path))
            return

        for i, n in enumerate(nums):
            if marked[i]:
                continue
            marked[i] = True
            path.append(n)
            self.dfs(nums, path, marked, ans)
            path.pop()
            marked[i] = False

47 - Permutations II

leetcode

Problem

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

Example:

Input: [1,1,2]
Output:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

Solution

Backtrack problem.

Framework of backtrack problem:

  1. choose a path
  2. selection pool
  3. return condition
ans = []

def backtrack(path, pool):

  if meet condition:
    ans.add(path)
    return

  for selection in pool:

    path.add(selection)
    backtrack(path, new_pool)
    path.remove(selection)

Important:

Pay attention, you must add a copy of the path to result, not the path it self. It may get modified afterwards.

class Solution:
    def permuteUnique(self, nums):
        ans = []
        self.backtrack([], nums, ans)
        return ans

    def backtrack(self, path, pool, ans):
        if not pool:
            ans.append(path[:])
            return

        t = {}

        for i, v in enumerate(pool):
            if v not in t:
                t[v] = 1
                self.backtrack(path+[v], pool[:i]+pool[i+1:], ans)

print(Solution().permuteUnique([1,1,2]))

1008 - Construct Binary Search Tree from Preorder Traversal

leetcode

Problem

Return the root node of a binary search tree that matches the given preorder traversal.

(Recall that a binary search tree is a binary tree where for every node, any descendant of node.left has a value < node.val, and any descendant of node.right has a value > node.val.  Also recall that a preorder traversal displays the value of the node first, then traverses node.left, then traverses node.right.)

Example 1:

Input: [8,5,1,7,10,12]
Output: [8,5,10,1,7,null,12]

Solution

We keep track of a low bound for the function.

We always build the right child with the low bound as the current node value.

If next value is smaller then current node value, we construct the left node.

If next value is smaller then current low bound, we construct the right node.

If next value is larger then current low bound, we jump out of the funciton.

Let the recursive jump back to the last level and repeat the same proccess

class Solution:
    def bstFromPreorder(self, preorder):
        if not preorder:
            return None
        root = TreeNode(preorder[0])
        self.buildTree(root, 1, preorder, sys.maxsize)
        return root

    def buildTree(self, root, pos, preorder, rmax):
        if pos >= len(preorder) or preorder[pos] > rmax:
            return None

        if preorder[pos] < root.val:
            root.left = TreeNode(preorder[pos])
            pos = self.buildTree(root.left, pos+1, preorder, root.val - 1)

        if pos < len(preorder) and preorder[pos] <= rmax:
            root.right = TreeNode(preorder[pos])
            pos = self.buildTree(root.right, pos+1, preorder, rmax)

        return pos

Concise version:

class Solution:
    pos = 0
    def bstFromPreorder(self, preorder, bound=float('inf')):
        if self.pos >= len(preorder) or preorder[self.pos] > bound:
            return None
        root = TreeNode(preorder[self.pos])
        self.pos += 1
        root.left = self.bstFromPreorder(preorder, root.val)
        root.right = self.bstFromPreorder(preorder, bound)
        return root

105 - Construct Binary Tree from Preorder and Inorder Traversal

leetcode

Problem

Given preorder and inorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

For example, given

preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
Return the following binary tree:

    3
   / \
  9  20
    /  \
   15   7

Solution

  1. The first node of the preorder is always the root.
  2. Find the root in the inorder array, split it there. The left part are all the nodes in left branch. the right part in the right branch.
  3. Repeat it recursively. Note that we should always pop the first node of preorder as current root(solution 1). All keep a global index as shown(solution 2).

Simple Version:

It uses a lot resources

  1. index() operation is also not O(1).
  2. inorder[:split] copies the sub array to the next level recursion.
class Solution:
    def buildTree(self, preorder, inorder):
        if inorder:
            node = TreeNode(preorder.pop(0))
            split = inorder.index(node.val)
            node.left = self.buildTree(preorder, inorder[:split])
            ndoe.right = self.buildTree(preorder, inorder[split+1:])
            return node

Optimized Version:

  1. use a map instead of index() operation
  2. pass the index instead of the sub array it self
class Solution(object):
    def __init__(self):
        self.map = {}

    def buildTree(self, preorder, inorder):
        for i, val in enumerate(inorder):
            self.map[val] = i

        return self.recursive(0, len(preorder) - 1, preorder, inorder)

    def recursive(self, start, end, preorder, inorder):
        if (start > end):
            return None

        node = TreeNode(preorder.pop(0))

        split = self.map[node.val]

        node.left = self.recursive(start, split - 1, preorder, inorder)
        node.right = self.recursive(split + 1, end, preorder, inorder)

        return node

Leftmost Column with at Least a One

leetcode

Problem

A binary matrix means that all elements are 0 or 1. For each individual row of the matrix, this row is sorted in non-decreasing order.

Given a row-sorted binary matrix binaryMatrix, return leftmost column index(0-indexed) with at least a 1 in it. If such index doesn't exist, return -1.

You can't access the Binary Matrix directly.  You may only access the matrix using a BinaryMatrix interface:

BinaryMatrix.get(x, y) returns the element of the matrix at index (x, y) (0-indexed).
BinaryMatrix.dimensions() returns a list of 2 elements [m, n], which means the matrix is m * n.
Submissions making more than 1000 calls to BinaryMatrix.get will be judged Wrong Answer.  Also, any solutions that attempt to circumvent the judge will result in disqualification.

For custom testing purposes you're given the binary matrix mat as input in the following four examples. You will not have access the binary matrix directly.

Example 1:

Input: mat = [[0,0],[1,1]]
Output: 0

Example 4:
Input: mat = [[0,0,0,1],[0,0,1,1],[0,1,1,1]]
Output: 1

Solution

# """
# This is BinaryMatrix's API interface.
# You should not implement it, or speculate about its implementation
# """
#class BinaryMatrix(object):
#    def get(self, x: int, y: int) -> int:
#    def dimensions(self) -> list[]:
class Solution:
    def leftMostColumnWithOne(self, binaryMatrix):
        n, m = binaryMatrix.dimensions()
        ans = m
        for i in range(n):
            x = self.findOne(binaryMatrix, i, 0, m - 1)
            if x != -1:
                ans = min(ans, x)
        return ans if ans != m else -1

    def findOne(self, m, r, i, j):
        if i == j:
            return i if m.get(r, i) == 1 else -1

        mid = (i + j) // 2
        if m.get(r, mid) == 1:
            if mid == 0 or m.get(r, mid - 1) == 0:
                return mid
            else:
                return self.findOne(m, r, i, mid - 1)
        else:
            return self.findOne(m, r, mid+1, j)

560 Subarray Sum Equals K

leetcode

Problem

Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k.

Example 1:
Input:nums = [1,1,1], k = 2
Output: 2
Note:
The length of the array is in range [1, 20,000].
The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7].

Solution

class Solution:
    def subArraySum(self, nums, k):
        h = {0: 1}
        s = 0
        ans = 0
        for i in nums:
            s += i
            ans += h.get(s-k, 0)
            h[s] = h.get(s, 0) + 1

        return ans

201 - Bitwise AND of Numbers Range

leetcode

Problem

Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers in this range, inclusive.

Example 1:

Input: [5,7]
Output: 4
Example 2:

Input: [0,1]
Output: 0

Solution

m = xxx1yyyy n = xxx01zzz

xxx is the parts that two numbers are the same. We can definitly find these two numbers in the range

m' = xxx1000 n' = xxx0100

So the result will be xxx0000

The idea is the find position where the the numbers are the same.

class Solution:
    def rangeBitwiseAnd(self, m, n):
        i = 0
        while m != n:
            m >>= 1
            n >>= 1
            i += 1
        return n << i

146 - LRU Cache

leetcode

Problem

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

The cache is initialized with a positive capacity.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

Solution

  1. How to manage to have a O(1) for get()?

    Hash Table

  2. How to manage to have a O(1) for put()?

    We have to somehow maintain its order, when something is added.

    Linked list would come to mind at first. But how we can change its order when we call get() on a node and also be able to delete the last one. We would like to know the previous one of a node.

    A Double Linked List is a perfect match.

class LRUCache:

    def __init__(self, capacity: int):
        self.count = 0
        self.capacity = capacity
        self.hash = {}
        self.head = Node(0, 0)
        self.tail = Node(0, 0)
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key in self.hash:
            node = self.hash[key]
            self._remove(node)
            self._add(node)
            return node.value
        else:
            return -1

    def put(self, key: int, value: int) -> None:
        if key not in self.hash:
            node = Node(key, value)
            self.hash[key] = node
            self.count += 1
            self._add(node)
        else:
            node = self.hash[key]
            node.value = value
            self.get(key)

        if self.count > self.capacity:
            n = self.tail.prev
            self.hash.pop(n.key)
            self._remove(n)
            self.count -= 1

    def _connect(self, p, n):
        p.next, n.prev = n, p

    def _add(self, n):
        tmp = self.head.next
        self._connect(self.head, n)
        self._connect(n, tmp)

    def _remove(self, node):
        self._connect(node.prev, node.next)



class Node:
    def __init__(self, key, value):
        self.key = key
        self.value =  value
        self.prev = None
        self.next = None


# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)

460 - LFU Cache

leetcode

Problem

Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.

Note that the number of times an item is used is the number of calls to the get and put functions for that item since it was inserted. This number is set to zero when the item is removed.



Follow up:
Could you do both operations in O(1) time complexity?



Example:

LFUCache cache = new LFUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.get(3);       // returns 3.
cache.put(4, 4);    // evicts key 1.
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

Solution

  1. O(1) get => Hash table for all nodes
  2. for keeping the oder by frequency, we can have another hash table. the key is the frequency. the value is a double linked list
  3. We need a min_freqeuncy to find the list of min frequency in O(1), and delete the last element of the list
class Node:
    def __init__(self, key, value):
        self.freq = 1
        self.key = key
        self.value = value
        self.next = None
        self.prev = None


class DoubleLinkedList:
    def __init__(self):
        self._head = Node(0, 0)
        self._tail = Node(0, 0)
        self._head.next = self._tail
        self._tail.prev = self._head
        self._size = 0

    def append(self, node):
        self._connect(node, self._head.next)
        self._connect(self._head, node)
        self._size += 1

    def pop(self, node):
        self._connect(node.prev, node.next)
        node.next = None
        node.prev = None
        self._size -= 1
        return node

    def pop_last(self):
        return self.pop(self._tail.prev)

    def get_size(self):
        return self._size

    def _connect(self, p, n):
        p.next, n.prev = n, p


class LFUCache:
    def __init__(self, capacity):
        self.nodes = {}
        self.freq = {}
        self.min_freq = 1
        self.capacity = capacity

    def get(self, key):
        if key in self.nodes:
            node = self.nodes[key]
            l = self.freq[node.freq]
            l.pop(node)
            if l.get_size() == 0 and self.min_freq == node.freq:
                self.min_freq += 1

            node.freq += 1
            if self.freq.get(node.freq):
                self.freq[node.freq].append(node)
            else:
                l = DoubleLinkedList()
                l.append(node)
                self.freq[node.freq] = l
            return node.value
        else:
            return -1

    def put(self, key, value):

        if self.capacity == 0:
            return

        if key in self.nodes:
            self.nodes[key].value = value
            self.get(key)
        else:
            node = Node(key, value)
            if len(self.nodes) == self.capacity:
                l = self.freq[self.min_freq]
                n = l.pop_last()
                self.nodes.pop(n.key)

            self.nodes[key] = node

            if 1 in self.freq:
                self.freq[1].append(node)
            else:
                l = DoubleLinkedList()
                l.append(node)
                self.freq[1] = l
            self.min_freq = 1


c = LFUCache(3)
c.put(1, 1)
c.put(2, 2)
print(c.get(1))
print(c.get(2))
print(c.get(1))
c.put(3, 3)
print(c.get(2))
print(c.get(3))
c.put(4, 4)

print(c.get(4))

437 - Path Sum III

leetcode

Problem

You are given a binary tree in which each node contains an integer value.

Find the number of paths that sum to a given value.

The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).

The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.

Example:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

Return 3. The paths that sum to 8 are:

1.  5 -> 3
2.  5 -> 2 -> 1
3. -3 -> 11

Solution

Important:

Three parts in recursion:

  1. the path sum of the left child
  2. the path sum of the right child
  3. the path sum only from the root
class Solution:
    def pathSum(self, root, sum):
        if not root:
            return 0

        return self.pathSum(root.left, sum) + self.pathSum(
            root.right, sum) + self.pathSumFrom(root, sum)

    def pathSumFrom(self, root, sum):

        if not root:
            return 0

        if root.val == sum:
            return 1 + self.pathSumFrom(root.left, 0) + self.pathSumFrom(
                root.right, 0)
        else:
            return self.pathSumFrom(root.left,
                                    sum - root.val) + self.pathSumFrom(
                                        root.right, sum - root.val)

1143 - Longest Common Subsequence

leetcode

Problem

Given two strings text1 and text2, return the length of their longest common subsequence.

A subsequence of a string is a new string generated from the original string with some characters(can be none) deleted without changing the relative order of the remaining characters.
(eg, "ace" is a subsequence of "abcde" while "aec" is not).
A common subsequence of two strings is a subsequence that is common to both strings.

If there is no common subsequence, return 0.

Example 1:

Input: text1 = "abcde", text2 = "ace"
Output: 3
Explanation: The longest common subsequence is "ace" and its length is 3.

Example 2:

Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.

Example 3:

Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no such common subsequence, so the result is 0.

Solution

The hardest part of this problem is to figure out what is the transition.

The current state should be determined by index i, j for each string respectively.

  1. States: dp[i][j] means the longest common subsequence for text1 til index i and text2 til index j

  2. Transition:

    if text1[i] = text2[j]: dp[i][j] = dp[i-1][j-1] if text1[i] ! text2[j]: dp[i][j] = max(dp[i-1][j], dp[i][j-1])

  3. Base Case: we need padding for i = 0 and j = 0

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        dp = [[0] * (len(text1) + 1) for i in range(len(text2) + 1)]

        for i in range(len(text2)):
            for j in range(len(text1)):
                if text2[i] == text1[j]:
                    dp[i + 1][j + 1] = dp[i][j] + 1
                else:
                    dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1])

        return dp[-1][-1]

887 - Super Egg Drop

leetcode

Problem

You are given K eggs, and you have access to a building with N floors from 1 to N.

Each egg is identical in function, and if an egg breaks, you cannot drop it again.

You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break, and any egg dropped at or below floor F will not break.

Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N).

Your goal is to know with certainty what the value of F is.

What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?

Example 1:

Input: K = 1, N = 2

Output: 2

Explanation:
Drop the egg from floor 1.  If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2.  If it breaks, we know with certainty that F = 1.
If it didn't break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.

Example 2:

Input: K = 2, N = 6

Output: 3

Example 3:

Input: K = 3, N = 14

Output: 4

Solution

Solution 1: dp

A naive approach with dp. Time consuming.

class Solution:
    def superEggDrop(self, K: int, N: int) -> int:
        memo = dict()

        def dp(K, N):
            if K == 1:
                return N
            if N == 0:
                return 0
            if (K, N) in memo:
                return memo[(K, N)]

            res = float('INF')

            for i in range(1, N+1):
                res = min(res, max(dp(K-1, i-1), dp(K, N - i)) + 1)

            memo[(K, N)] = res
            return res

        return dp(K, N)

Time: O(K*N*N)

Space: O(K*N)

Improve the previous one with a binary search

class Solution:
    def superEggDrop(self, K: int, N: int) -> int:
        memo = dict()

        def dp(K, N):
            if K == 1:
                return N
            if N == 0:
                return 0
            if (K, N) in memo:
                return memo[(K, N)]

            res = float('INF')
            lo = 1
            hi = N
            while lo <= hi:
                mid = (lo + hi) // 2
                broken = dp(K - 1, mid - 1)
                not_broken = dp(K, N - mid)
                if broken > not_broken:
                    hi = mid - 1
                    res = min(res, broken + 1)
                else:
                    lo = mid + 1
                    res = min(res, not_broken + 1)

            memo[(K, N)] = res
            return res

        return dp(K, N)
print(Solution().superEggDrop(3, 14))

Time: O(K*N*logN)

Space: O(K*N)

Solution 3: dp with moves*eggs

Use a different transition.

Think about the problem as, given K eggs and M moves, what N can you at least reach.

class Solution:
    def superEggDrop(self, K, N):
      dp = [[0]*(K+1) for _ in range(N+1)]
      m = 0
      while dp[m][K] < N:
          m += 1
          for i in range(1, K+1):
              dp[m][i] = dp[m-1][i] + dp[m-1][i-1] + 1
      return m

print(Solution().superEggDrop(2, 6))
print(Solution().superEggDrop(3, 14))

Time: O(K*N)

Space: O(K*N)

The transition is only related two previous value, we can reduce the dp matrix two O(1).

class Solution:
    def superEggDrop(self, K, N):
      m = 0
      dp = [0] * (K+1)
      while dp[K] < N:
          m += 1
          prev = 0
          for i in range(1, K+1):
              tmp = dp[i]
              dp[i] = dp[i] + prev + 1
              prev = tmp
      return m

28 - Implement strStr() (KMP Algorithm)

leetcode

Problem

Implement strStr().

Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Example 1:

Input: haystack = "hello", needle = "ll"
Output: 2
Example 2:

Input: haystack = "aaaaa", needle = "bba"
Output: -1

Solution

KMP tutorial

class Solution:
    def __init__(self, pat):
        self.pat = pat
        self.dp = []
        self.KMP(self.pat)

    def KMP(self, pat):

        M = len(pat)
        self.dp = [[0] * 256 for _ in range(M)]
        self.dp[0][ord(pat[0])] = 1
        X = 0

        for j in range(1, M):
            for c in range(256):
                if ord(pat[j]) == c:
                    self.dp[j][c] = j + 1
                else:
                    self.dp[j][c] = self.dp[X][c]

            X = self.dp[X][ord(pat[j])]

    def search(self, txt):
        M = len(self.pat)
        N = len(txt)
        s = 0

        for i in range(N):
            s = self.dp[s][ord(txt[i])]
            if s == M:
                return i - M + 1
        return -1

sol = Solution("ababc")

print(sol.search("ababdabababc"))

169 - Majority Element

leetcode

Problem

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

You may assume that the array is non-empty and the majority element always exist in the array.

Example 1:

Input: [3,2,3] Output: 3

Example 2:

Input: [2,2,1,1,1,2,2] Output: 2

Solution

Solution 1: Hash Table

from collections import Counter
class Solution:
    def majorityElement(self, nums):
        c = Counter()
        l = len(nums)
        for n in nums:
            c[n] += 1
            if c[n] > l/2:
                return n

print(Solution().majorityElement([1, 1, 2, 2, 2, 2, 4]))

Time: O(n)

Space: O(n)

Solution 2: Boyer-Moore Majority Vote Algorithm

http://www.cs.utexas.edu/~moore/best-ideas/mjrty/index.html

This algorithm works only when the majorty exists.

For array like [1, 1, 2, 2, 2, 1], it return 2. But it is not a majority element.

class Solution:
    def majorityElement(self, nums):
        counter = 1
        ans = nums[0]

        for n in nums[1:]:
            if n == ans:
                counter += 1
            else:
                counter -= 1
                if counter < 0:
                    counter = 1
                    ans = n
        return ans
print(Solution().majorityElement([1, 1, 2, 2, 2, 2, 4]))

Time: O(n)

Space: O(1)

448 - Find All Numbers Disappeard in an Array

leetcode

Problem

Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.

Find all the elements of [1, n] inclusive that do not appear in this array.

Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.

Example:

Input: [4,3,2,7,8,2,3,1]

Output: [5,6]

Solution

Move all the numbers to its position. For example, 4 move to index 3, 3 move to index 2 and so on.

The numbers at index i, which are not equal i + 1, are the ones disappeared.

The operation is in-place.

class Solution:
    def findDisappearedNumbers(self, nums):
        for i in nums:
            j = i
            while nums[j-1] != j:
                nums[j-1], j = j, nums[j-1]

        return [i+1 for i in range(len(nums)) if nums[i] != i+1]

Time: O(N)

Space: O(1)

124 - Binary Tree Maximum Path Sum

leetcode

Problem

Given a non-empty binary tree, find the maximum path sum.

For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.

Example 1:

Input: [1,2,3]

1 / \
2 3

Output: 6 Example 2:

Input: [-10,9,20,null,null,15,7]

-10 / \
9 20 / \
15 7

Output: 42

Solution

Important:

Remember we should find a path, not a sub tree.

Solution 1: Hash

from collections import defaultdict

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    h = defaultdict(int)

    def maxPathSum(self, root: TreeNode) -> int:
        if not root:
            return -sys.maxsize

        return max(self.maxPathSumFrom(root), self.maxPathSum(root.left),
                   self.maxPathSum(root.right))

    def maxPathSumFrom(self, root):

        if not root:
            return 0

        return max(
            root.val, root.val + self.maxPathSumDfs(root.right),
            root.val + self.maxPathSumDfs(root.left), root.val +
            self.maxPathSumDfs(root.right) + self.maxPathSumDfs(root.left))

    def maxPathSumDfs(self, root):
        if not root:
            return 0

        if root in self.h:
            return self.h[root]
        else:
            rightSum = self.h.get(root.right, self.maxPathSumDfs(root.right))
            leftSum = self.h.get(root.left, self.maxPathSumDfs(root.left))
            ans = max(root.val, root.val + rightSum, root.val + leftSum)
            self.h[root] = ans

        return ans

Solution 2: Global maximum with dfs

The tricky part here is to update the global maximum always with both left branch and right branch, but dfs only returns with the larger branch of the node, because we have to keep the left and right side as a “path” not a “tree

Version 1:

class Solution:
    ans = -sys.maxsize

    def maxPathSum(self, root):
        self.dfs(root)
        return self.ans

    def dfs(self, root):
        if not root:
            return 0

        left = max(self.dfs(root.left), 0)
        right = max(self.dfs(root.right), 0)

        self.ans = max(self.ans, root.val + left + right)
        return root.val + max(left, right)

Version 2: I find it easier to understand

class Solution:
    ans = -sys.maxsize

    def maxPathSum(self, root):
        self.dfs(root)
        return self.ans

    def dfs(self, root):
        if not root:
            return 0

        left = self.dfs(root.left)
        right = self.dfs(root.right)

        val = max(root.val, root.val + left, root.val + right)
        self.ans = max(self.ans, val, root.val + left + right)

        return val

278 - First Bad Version

leetcode

Problem

You are a product manager and currently leading a team to develop a new product.
Unfortunately, the latest version of your product fails the quality check.
Since each version is developed based on the previous version, all the versions after a bad version are also bad.

Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.

You are given an API bool isBadVersion(version) which will return whether version is bad.
Implement a function to find the first bad version. You should minimize the number of calls to the API.

Example:

Given n = 5, and version = 4 is the first bad version.

call isBadVersion(3) -> false
call isBadVersion(5) -> true
call isBadVersion(4) -> true

Then 4 is the first bad version.

Solution

Binary Search is your friend!

Solution 1: Binary Search to the end

class Solution:
    def firstBadVerion(self, n):
        l, r, mid = 1, n, (1+n) // 2

        while l != mid:
            if isBadVersion(mid):
                l = mid
            else:
                r = mid
            mid = (l + r) // 2

        return l if isBadVersion(l) else r

Solution 2: Binary Search with break

It should be faster then solution 1, because we do not wait for l meets mid, which might take longer.

class Solution:
    def firstBadVersion(self, n):
        if n == 1:
            return 1

        l, r = 2, n

        while l <= r:
            mid = (l + r) // 2
            if isBadVersion(mid) and not isBadVersion(mid - 1):
                return mid
            elif isBadVersion(mid):
                r = mid - 1
            else:
                l = mid + 1
        return 1

106 - Construct Binary Tree from Inorder and Postorder Traversal

leetcode

Problem

Given inorder and postorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

For example, given

inorder = [9,3,15,20,7]
postorder = [9,15,7,20,3]
Return the following binary tree:

    3
   / \
  9  20
    /  \
   15   7

Solution

The problem is almost the same as 105 Construct BT from Preorder and Inorder. Notes can be found there.

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        if inorder:
            node = TreeNode(postorder.pop())
            split = inorder.index(node.val)
            node.right = self.buildTree(inorder[split + 1:], postorder)
            node.left = self.buildTree(inorder[:split], postorder)

            return node

128 - Longest Consecutive Sequence

leetcode

Problem

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.

Your algorithm should run in O(n) complexity.

Example:

Input: [100, 4, 200, 1, 3, 2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

Solution

Solution 1: Hash Map

For each value, we maintain the left bound and right bound of range and also update the bounds for left value and right value.

class Solution:
    def longestConsecutive(self, nums):
        h = dict()
        ans = 0

        for num in nums:
            if num not in h:
                if num - 1 in h and num + 1 in h:
                    h[h[num + 1][1]][0] = h[num - 1][0]
                    h[h[num - 1][0]][1] = h[num + 1][1]
                    h[num] = [h[num - 1][0], h[num + 1][1]]
                elif num - 1 in h:
                    h[h[num - 1][0]][1] += 1
                    h[num] = h[h[num - 1][0]]
                elif num + 1 in h:
                    h[h[num + 1][1]][0] -= 1
                    h[num] = h[h[num + 1][1]]
                else:
                    h[num] = [num, num]
                ans = max(ans, h[num][1] - h[num][0] + 1)

        return ans

Time: O(n)

Space: O(n)

Solution 2: Keep Counting!

Notice the if x - t not in nums.

class Solution:
    def longestConsecutive(self, nums):
        ans = 0
        nums = set(nums)
        for x in nums:
            if x - 1 not in nums:
                y = x + 1
                while y in nums:
                    y += 1
                ans = max(ans, y - x)
        return ans

Time: O(n)

Space: O(n)

98 - Validate Binary Search Tree

leetcode

Problem

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

- The left subtree of a node contains only nodes with keys less than the node's key.
- The right subtree of a node contains only nodes with keys greater than the node's key.
- Both the left and right subtrees must also be binary search trees.

Example 1:

    2
   / \
  1   3

Input: [2,1,3]
Output: true

Example 2:

    5
   / \
  1   4
     / \
    3   6

Input: [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.

Solution

Important:

You don’t only valid the current node with its left and right value. You have to:

class Solution:
    def isValidBST(self, root):
        return self.isValidBSTBound(root, -sys.maxsize, sys.maxisze)

    def isValidBSTBound(self, root, lb, hb):
        if not root:
            return True

        if lb < root.val < hb:
            return self.isValidBSTBound(root.left, lb, root.val) and self.isValidBSTBound(root.right, root.val, hb)
        else:
            return False

1009 - Complement of Base 10 Integer

leetcode

Problem

Every non-negative integer N has a binary representation.  For example, 5 can be represented as "101" in binary, 11 as "1011" in binary, and so on.  Note that except for N = 0, there are no leading zeroes in any binary representation.

The complement of a binary representation is the number in binary you get when changing every 1 to a 0 and 0 to a 1.  For example, the complement of "101" in binary is "010" in binary.

For a given number N in base-10, return the complement of it's binary representation as a base-10 integer.

Example 1:

Input: 5
Output: 2
Explanation: 5 is "101" in binary, with complement "010" in binary, which is 2 in base-10.

Example 2:

Input: 7
Output: 0
Explanation: 7 is "111" in binary, with complement "000" in binary, which is 0 in base-10.

Example 3:

Input: 10
Output: 5
Explanation: 10 is "1010" in binary, with complement "0101" in binary, which is 5 in base-10.

Solution

Solution 1: XOR

Watch out the corner case when num is 0.

class Solution:
    def bitwiseComplement(self, num: int) -> int:
        if num == 0:
            return 1

        c = 1
        n = num
        while n:
            c *= 2
            n >>= 1

        return num ^ (c - 1)

Solution 2:

x = 2 * x + 1

1 = 1
1*2 + 1 = 3 = 11
3*2 + 1 = 7 = 111
7*2 + 1 = 15 = 1111
class Solution:
    def bitwiseComplement(self, num):
        x = 1
        while x < num: x = 2 * x + 1
        return x - num

337 - House Robber III

leetcode

Problem

The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.

Determine the maximum amount of money the thief can rob tonight without alerting the police.

Example 1:

Input: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \
     3   1

Output: 7
Explanation: Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
Example 2:

Input: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \
 1   3   1

Output: 9
Explanation: Maximum amount of money the thief can rob = 4 + 5 = 9.

Solution

We can do it recursively with a memo, to reduce the search depth.

On every node, we have 2 statuses, which will determine our next move:

class Solution:
    memo = {}
    def rob(self, root):
        return self.robRec(root, False)

    def robRec(self, root, isRobbed):
        if not root:
            return 0

        if (root, isRobbed) in memo:
            return memo[(root, isRobbed)]

        if isRobbed:
            ans = self.robRec(root.left, False) + self.robRec(root.right, False)
        else:
            ans = max(root.val + self.robRec(root.left, True) + self.robRec(root.right, True),
                      self.robRec(root.left, False) + self.robRec(root.right, False))

        memo[(root, isRobbed)] = ans

        return ans

Cousins in Binary Tree

leetcode

Problem


In a binary tree, the root node is at depth 0, and children of each depth k node are at depth k+1.

Two nodes of a binary tree are cousins if they have the same depth, but have different parents.

We are given the root of a binary tree with unique values, and the values x and y of two different nodes in the tree.

Return true if and only if the nodes corresponding to the values x and y are cousins.

Example 1:

Input: root = [1,2,3,4], x = 4, y = 3
Output: false
Example 2:

Input: root = [1,2,3,null,4,null,5], x = 5, y = 4
Output: true
Example 3:

Input: root = [1,2,3,null,4], x = 2, y = 3
Output: false

Note:

The number of nodes in the tree will be between 2 and 100.
Each node has a unique integer value from 1 to 100.

Solution

class Solution:
    m = {}

    def isCousins(self, root, x, y):
        if not root:
            return False

        if self.depth(root, x) == self.depth(root, y):
            return self.m[x] != self.m[y]

        return False

    def depth(self, root, val):
        if not root:
            return -1

        if root.val == val:
            return 0
        else:

            left = self.depth(root.left, val)
            right = self.depth(root.right, val)

            if left >= 0:
                self.m[root.left.val] = root.val
                return 1 + left
            elif right >= 0:
                self.m[root.right.val] = root.val
                return 1 + right

        return -1

1232 - Check If It Is a Straight Line

leetcode

Problem

You are given an array coordinates, coordinates[i] = [x, y],
where [x, y] represents the coordinate of a point.

Check if these points make a straight line in the XY plane.

Solution

Important

Notice the divider. It may be zero, when the lines are horizontal.

class Solution:
    def checkStraightLine(self, coordinates: List[List[int]]) -> bool:
        p1 = coordinates[0]
        p2 = coordinates[1]
        slope = self.slope(p1, p2)

        for i in range(2, len(coordinates)):
            if self.slope(coordinates[0], coordinates[i]) != slope:
                return False

        return True

    def slope(self, p1, p2):
        if p1[1] == p2[1]:
            return sys.maxsize

        return (p1[0] - p2[0]) / (p1[1] - p2[1])

733 - Flood Fill

leetcode

Problem

An image is represented by a 2-D array of integers, each integer representing the pixel value of the image (from 0 to 65535).

Given a coordinate (sr, sc) representing the starting pixel (row and column) of the flood fill, and a pixel value newColor, "flood fill" the image.

To perform a "flood fill", consider the starting pixel, plus any pixels connected 4-directionally to the starting pixel of the same color as the starting pixel, plus any pixels connected 4-directionally to those pixels (also with the same color as the starting pixel), and so on. Replace the color of all of the aforementioned pixels with the newColor.

At the end, return the modified image.

Example 1:
Input:
image = [[1,1,1],[1,1,0],[1,0,1]]
sr = 1, sc = 1, newColor = 2
Output: [[2,2,2],[2,2,0],[2,0,1]]
Explanation:
From the center of the image (with position (sr, sc) = (1, 1)), all pixels connected
by a path of the same color as the starting pixel are colored with the new color.
Note the bottom corner is not colored 2, because it is not 4-directionally connected
to the starting pixel.
Note:

The length of image and image[0] will be in the range [1, 50].
The given starting pixel will satisfy 0 <= sr < image.length and 0 <= sc < image[0].length.
The value of each color in image[i][j] and newColor will be an integer in [0, 65535].

Solution

Stack.

  1. Add the starting point to stack
  2. Stack pop. Fill the point, add its valid neighbours to the stack.
  3. Repeat 2 until stack is empty

Important:

When the color to fill is the same as the starting point. We don’t need to continue. Otherwise you will be always adding the same points again and again into the stack.

class Solution:
    def floodFill(self, image, sr: int, sc: int, newColor: int):
        oldColor = image[sr][sc]
        stack = []
        if newColor != oldColor:
            stack.append([sr, sc])

        m = len(image)
        n = len(image[0])

        while stack:
            p = stack.pop()
            x, y = p

            image[x][y] = newColor

            for v in [[1, 0], [0, 1]]:
                for s in [1, -1]:
                    r = v[0] * s
                    c = v[1] * s
                    if 0 <= x + r < m and 0 <= y + c < n and image[x + r][y + c] == oldColor:
                        stack.append([x + r, y + c])

        return image

997 - Find the Town Judge

leetcode

Problem

In a town, there are N people labelled from 1 to N.
There is a rumor that one of these people is secretly the town judge.

If the town judge exists, then:

1. The town judge trusts nobody.
2. Everybody (except for the town judge) trusts the town judge.
3. There is exactly one person that satisfies properties 1 and 2.

You are given trust, an array of pairs trust[i] = [a, b] representing that the person labelled a trusts the person labelled b.

If the town judge exists and can be identified, return the label of the town judge.  Otherwise, return -1.

Example 1:

Input: N = 2, trust = [[1,2]]
Output: 2

Example 2:

Input: N = 3, trust = [[1,3],[2,3]]
Output: 3

Example 3:

Input: N = 3, trust = [[1,3],[2,3],[3,1]]
Output: -1

Example 4:

Input: N = 3, trust = [[1,2],[2,3]]
Output: -1

Example 5:

Input: N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
Output: 3


Note:

1 <= N <= 1000
trust.length <= 10000
trust[i] are all different
trust[i][0] != trust[i][1]
1 <= trust[i][0], trust[i][1] <= N

Solution

Two tables:

  1. one for counting how many trusts each person gets
  2. one for counting how many other people each person trusts
class Solution:
    def findJudge(self, N: int, trust: List[List[int]]) -> int:
        people = [0] * (N)
        h = defaultdict(int)

        for t in trust:
            people[t[1]-1] += 1
            h[t[0]-1] = 1

        for i in range(N):
            if people[i] >= N - 1 and h[i] == 0:
                return i+1

        return -1

540 - Single Element in a Sorted Array

leetcode

Problem

You are given a sorted array consisting of only integers where every element appears exactly twice, except for one element which appears exactly once. Find this single element that appears only once.

Example 1:

Input: [1,1,2,3,3,4,4,8,8] Output: 2

Example 2:

Input: [3,3,7,7,10,11,11] Output: 10

Solution

class Solution:
    def singleNonDuplicate(self, nums):
        lo = 0
        hi = len(nums) - 1

        while lo < hi:
            mid = (lo + hi) // 2
            if mid == 0:
                return mid

            if (mid - lo + 1) % 2 == 0:
                if nums[mid] == nums[mid - 1]:
                    lo = mid + 1
                else:
                    hi = mid - 1
            else:
                if nums[mid] == nums[mid - 1]:
                    hi = mid - 2
                else:
                    lo = mid
        return nums[lo]

print(Solution().singleNonDuplicate([1,1,2,3,3,5,5,10,10]))

402 - Remove K Digits

leetcode

Problem

Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible.

Note: The length of num is less than 10002 and will be ≥ k. The given num does not contain any leading zero.

Example 1:

Input: num = “1432219”, k = 3 Output: “1219” Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest.

Example 2:

Input: num = “10200”, k = 1 Output: “200” Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes.

Example 3:

Input: num = “10”, k = 2 Output: “0” Explanation: Remove all the digits from the number and it is left with nothing which is 0.

Solution

Stack

Corner cases:

‘1234567’ k = 3: k will not be reduced to 0. So stack[:-k or None] ‘10200’ k = 1: k can be reduced to 0. stack[:-0] will result in []. So stack[:-k or None] ‘1000000’ k = 1: ……lstrip(‘0’) will result 0. So we have to add or '0'

class Solution:
    def removeKdigits(self, num, k):
        stack = []
        for c in num:
            while k and stack and stack[-1] > c:
                stack.pop()
                k -= 1
            stack.append(c)

        return ''.join(stack[:-k or None]).lstrip('0') or '0'

208 - Implement Trie (Prefix Tree)

leetcode

Problem

Implement a trie with insert, search, and startsWith methods.

Example:

Trie trie = new Trie();

trie.insert(“apple”); trie.search(“apple”); / returns true trie.search(“app”); / returns false trie.startsWith(“app”); // returns true trie.insert(“app”); trie.search(“app”); // returns true

Solution

Trie.

Use a “END” Symbol for word ending.

search method checks for “END”, startsWith not.

class Trie:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = Node()


    def insert(self, word: str) -> None:
        """
        Inserts a word into the trie.
        """
        node = self.root
        for c in word:
            node = node.next[c]

        node.isWord = True

    def search(self, word: str) -> bool:
        """
        Returns if the word is in the trie.
        """
        node = self.root

        for c in word:
            node = node.next.get(c)
            if not node:
                return False

        return node.isWord


    def startsWith(self, prefix: str) -> bool:
        """
        Returns if there is any word in the trie that starts with the given prefix.
        """
        node = self.root
        for c in prefix:
            node = node.next.get(c)
            if not node:
                return False

        return True

class Node:
    def __init__(self):
        self.next = defaultdict(Node)
        self.isWord = False


s = Trie()

s.insert("apple")
s.insert("banana")
print(s.search("app"))
print(s.search("bananananana"))
print(s.search("banana"))
print(s.startsWith("ban"))

918 - Maximum Sum Circular Subarray

leetcode

Problem

Given a circular array C of integers represented by A, find the maximum possible sum of a non-empty subarray of C.

Here, a circular array means the end of the array connects to the beginning of the array. (Formally, C[i] = A[i] when 0 <= i < A.length, and C[i+A.length] = C[i] when i >= 0.)

Also, a subarray may only include each element of the fixed buffer A at most once. (Formally, for a subarray C[i], C[i+1], …, C[j], there does not exist i <= k1, k2 <= j with k1 % A.length = k2 % A.length.)

Example 1:

Input: [1,-2,3,-2] Output: 3 Explanation: Subarray [3] has maximum sum 3

Example 2:

Input: [5,-3,5] Output: 10 Explanation: Subarray [5,5] has maximum sum 5 + 5 = 10

Example 3:

Input: [3,-1,2,-1] Output: 4 Explanation: Subarray [2,-1,3] has maximum sum 2 + (-1) + 3 = 4

Example 4:

Input: [3,-2,2,-3] Output: 3 Explanation: Subarray [3] and [3,-2,2] both have maximum sum 3

Example 5:

Input: [-2,-3,-1] Output: -1 Explanation: Subarray [-1] has maximum sum -1

Note:

-30000 <= A[i] <= 30000 1 <= A.length <= 30000

Solution

Use dp we can solve the problem without circular easily. See problem 53. Maximum Subarray.

So we have a maxSum.

There are only two possibility where the sub array locates.

  1. The sub array is in the loop of array

    —-[—–]—– ————

    It’s maxSum.

  2. The sub array connects the next loop.

    ———-[– —]———-

    we can rearrange it to:

    [—]——[–]

    The the part outside the maximum subarray is “minimum subarray”.

    And we can calculate the minSum with the same algorithm for maxSum.

The results is

max(maxSum, s - minSum)

Corner case:

When all elements are negative, the maxSum is also negative. and s - minSum would be 0. max(maxSum, s - minSum) is 0.

But according to the description, we should return maxSum.

class Solution:
    def maxSubarraySumCircular(self, A):
        curMin = 0
        curMax = 0
        minSum = sys.maxsize
        maxSum = -sys.maxsize
        s = 0
        for c in A:
            s += c
            curMax = max(c, curMax + c)
            maxSum = max(maxSum, curMax)
            curMin = min(c, curMin + c)
            minSum = min(minSum, curMin)

        return max(maxSum, s - minSum) if maxSum > 0 else maxSum

328 - Odd Even Linked List

leetcode

Problem

Given a singly linked list, group all odd nodes together followed by the even nodes.
Please note here we are talking about the node number and not the value in the nodes.

You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.

Example 1:

Input: 1->2->3->4->5->NULL
Output: 1->3->5->2->4->NULL

Example 2:

Input: 2->1->3->5->6->4->7->NULL
Output: 2->3->6->7->1->5->4->NULL
Note:

The relative order inside both the even and odd groups should remain as it was in the input.
The first node is considered odd, the second node even and so on ...

Solution

class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head or not head.next or not head.next.next:
            return head

        p1, p2 = head, head.next
        even = p2

        while p2 and p2.next:
            p1.next = p2.next
            p1 = p1.next
            p2.next = p1.next
            p2 = p2.next

        p1.next = even

        return head

438 - Find All Anagrams in a String

leetcode

Problem

Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.

Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100.

The order of output does not matter.

Example 1:

Input:
s: "cbaebabacd" p: "abc"

Output:
[0, 6]

Explanation:
The substring with start index = 0 is "cba", which is an anagram of "abc".
The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2:

Input:
s: "abab" p: "ab"

Output:
[0, 1, 2]

Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".

Solution

from collections import defaultdict

class Solution:
    def findAllAnagrams(self, s, p):
        target, window = defaultdict(int), defaultdict(int)
        left, right = 0, 0
        match = 0
        ans = []

        for c in p:
            target[c] += 1

        while right < len(s):
            c = s[right]
            if c in target:
                window[c] += 1
                if window[c] == target[c]:
                    match += 1

            right += 1

            while right - left + 1 > len(p):
                if match == len(target):
                    ans.append(left)
                c = s[left]
                left += 1

                if c in window:
                    if window[c] == target[c]:
                        match -= 1
                    window[c] -= 1


        return ans

print(Solution().findAllAnagrams("cbaeabac", "abc"))

567 - Permutation in String

leetcode

Problem

Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1.
In other words, one of the first string's permutations is the substring of the second string.

Example 1:

Input: s1 = "ab" s2 = "eidbaooo"
Output: True
Explanation: s2 contains one permutation of s1 ("ba").

Example 2:

Input:s1= "ab" s2 = "eidboaoo"
Output: False


Note:

The input strings only contain lower case letters.
The length of both given strings is in range [1, 10,000].
Accepted

Solution

Sliding window.

from collections import defaultdict


class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        target, window = defaultdict(int), defaultdict(int)
        left, right = 0, 0
        match = 0
        for c in s1:
            target[c] += 1

        while (right < len(s2)):

            c = s2[right]
            if c in target:
                window[c] += 1
                if window[c] == target[c]:
                    match += 1

            right += 1

            while (right - left + 1 > len(s1)):
                if match == len(target):
                    return True
                c = s2[left]
                left += 1

                if c in window:
                    if window[c] == target[c]:
                        match -= 1

                    window[c] -= 1
        return False

901 - Onine Stock Span

leetcode

Problem

Write a class StockSpanner which collects daily price quotes for some stock, and returns the span of that stock's price for the current day.

The span of the stock's price today is defined as the maximum number of consecutive days (starting from today and going backwards) for which the price of the stock was less than or equal to today's price.

For example, if the price of a stock over the next 7 days were [100, 80, 60, 70, 60, 75, 85], then the stock spans would be [1, 1, 1, 2, 1, 4, 6].



Example 1:

Input: ["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
Output: [null,1,1,1,2,1,4,6]
Explanation:
First, S = StockSpanner() is initialized.  Then:
S.next(100) is called and returns 1,
S.next(80) is called and returns 1,
S.next(60) is called and returns 1,
S.next(70) is called and returns 2,
S.next(60) is called and returns 1,
S.next(75) is called and returns 4,
S.next(85) is called and returns 6.

Note that (for example) S.next(75) returned 4, because the last 4 prices
(including today's price of 75) were less than or equal to today's price.

Note:

Calls to StockSpanner.next(int price) will have 1 <= price <= 10^5.
There will be at most 10000 calls to StockSpanner.next per test case.
There will be at most 150000 calls to StockSpanner.next across all test cases.
The total time limit for this problem has been reduced by 75% for C++, and 50% for all other languages.

Solution

Stack problem.

Pop all the smaller one and calculate the result before pushing current one into stack.

class StockSpanner:

    def __init__(self):
        self.stack=[]

    def next(self, price):
        res=1
        while self.stack and self.stack[-1][0]<=price:
            res+=self.stack.pop()[1]

        self.stack.append([price,res])

        return res

230 - Kth Smallest Element in a BST

leetcode

Problem

230. Kth Smallest Element in a BST
Medium

2239

57

Add to List

Share
Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.

Note:
You may assume k is always valid, 1 ≤ k ≤ BST's total elements.

Example 1:

Input: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
Output: 1
Example 2:

Input: root = [5,3,6,2,4,null,null,1], k = 3
       5
      / \
     3   6
    / \
   2   4
  /
 1
Output: 3

Solution

class Solution:
    def kthSmallest(self, root):
        self.k = k
        self.ans = root.val
        traverse(root)
        return self.ans

    def traverse(self, root):
        if not root:
            return
        traverse(root.left)

        self.k -= 1
        if not self.k:
            self.ans = root.val
        else:
            traverse(root.right)

1277 - Count Square Submatrices with All Ones

leetcode

Problem

Given a m * n matrix of ones and zeros, return how many square submatrices have all ones.



Example 1:

Input: matrix =
[
  [0,1,1,1],
  [1,1,1,1],
  [0,1,1,1]
]
Output: 15
Explanation:
There are 10 squares of side 1.
There are 4 squares of side 2.
There is  1 square of side 3.
Total number of squares = 10 + 4 + 1 = 15.

Example 2:

Input: matrix =
[
  [1,0,1],
  [1,1,0],
  [1,1,0]
]
Output: 7
Explanation:
There are 6 squares of side 1.
There is 1 square of side 2.
Total number of squares = 6 + 1 = 7.

Solution

DP problem. Same as p227.

class Solution:
    def countSquares(self, matrix):
        if not matrix or not matrix[0]:
            return 0
        m = len(matrix)
        n = len(matrix[0])
        ans = 0
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 1:
                    if i and j:
                        matrix[i][j] = min(matrix[i-1][j-1], matrix[i][j-1], matrix[i-1][j]) + 1
                    ans += matrix[i][j]
        return ans

76 - Minimum Window Substring

leetcode

Problem

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

Example:

Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
Note:

If there is no such window in S that covers all characters in T, return the empty string "".
If there is such window, you are guaranteed that there will always be only one unique minimum window in S.

Solution

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        target, window = defaultdict(int), defaultdict(int)
        left, right, match = 0, 0, 0
        d = float("inf")
        for c in t:
            target[c] += 1
        while right < len(s):
            c = s[right]
            if c in target:
                window[c] += 1
                if window[c] == target[c]:
                    match += 1
            right += 1

            while (match == len(target)):
                if right - left < d:
                    ans = s[left:right]
                    d = right - left

                c = s[left]
                left += 1

                if c in target:
                    if window[c] == target[c]:
                        match -= 1
                    window[c] -= 1

        return "" if d == float("inf") else ans

451 - Sort Characters By Frequency

leetcode

Problem

Given a string, sort it in decreasing order based on the frequency of characters.

Example 1:

Input:
"tree"

Output:
"eert"

Explanation:
'e' appears twice while 'r' and 't' both appear once.
So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.

Example 2:

Input:
"cccaaa"

Output:
"cccaaa"

Explanation:
Both 'c' and 'a' appear three times, so "aaaccc" is also a valid answer.
Note that "cacaca" is incorrect, as the same characters must be together.

Example 3:

Input:
"Aabb"

Output:
"bbAa"

Explanation:
"bbaA" is also a valid answer, but "Aabb" is incorrect.
Note that 'A' and 'a' are treated as two different characters.

Solution

  1. Use a map to count the characters.
  2. Loop the map to the characters into a map using the frequency as key. The value will be a list, containing all the characters with the same frequency.
  3. Go through the map.

Solution 1: Two maps

from collections import defaultdict
class Solution:
    def frequencySort(self, s: str) -> str:
        cnt = defaultdict(int)
        freq = {}
        max_f = 0
        ans = ""

        for c in s:
            cnt[c] += 1

        for c, f in cnt.items():
            if f not in freq:
                freq[f] = []

            freq[f].append(c)
            max_f = max(max_f, f)

        for i in range(max_f, 0, -1):
            if i in freq:
                for c in freq[i]:
                    ans += c * i

        return ans

Solution 2: One liner

from collections import Counter
class Solution:
    def frequencySort(self, s):
        return "".join([c * cnt for c, cnt in Counter(s).most_common()])

Similar Problems

347. Top K Frequent Element

Given a non-empty array of integers, return the k most frequent elements.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:

Input: nums = [1], k = 1
Output: [1]
Note:

You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
It's guaranteed that the answer is unique, in other words the set of the top k frequent elements is unique.
You can return the answer in any order.

Solution

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        return [ i for i, cnt in Counter(nums).most_common()[:k]]

692. Top K Frequent Words

Given a non-empty list of words, return the k most frequent elements.

Your answer should be sorted by frequency from highest to lowest. If two words have the same frequency, then the word with the lower alphabetical order comes first.

Example 1:
Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
Output: ["i", "love"]
Explanation: "i" and "love" are the two most frequent words.
    Note that "i" comes before "love" due to a lower alphabetical order.
Example 2:
Input: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
Output: ["the", "is", "sunny", "day"]
Explanation: "the", "is", "sunny" and "day" are the four most frequent words,
    with the number of occurrence being 4, 3, 2 and 1 respectively.
Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Input words contain only lowercase letters.
Follow up:
Try to solve it in O(n log k) time and O(n) extra space.

Solution

class Solution:
    def topKFrequent(self, words: List[str], k: int) -> List[str]:
        counter = Counter(words)
        return heapq.nsmallest(k,
                               counter,
                               key=lambda word: (-counter[word], word))

986 - Interval List Intersections

leetcode

Problem

Given two lists of closed intervals, each list of intervals is pairwise disjoint and in sorted order.

Return the intersection of these two interval lists.

(Formally, a closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b.  The intersection of two closed intervals is a set of real numbers that is either empty, or can be represented as a closed interval.  For example, the intersection of [1, 3] and [2, 4] is [2, 3].)

Example 1:

Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]]
Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
Reminder: The inputs and the desired output are lists of Interval objects, and not arrays or lists.

Solution

class Solution:
    def intervalListIntersection(self, A, B):
        ans = []
        i = j = 0

        while i < len(A) and j < len(B):
            left = max(A[i][0], B[j][0])
            right = min(A[i][1], B[j][1])
            if left <= right:
                ans.append([left, right])
            if A[i][1] < B[j][1]:
                i += 1
            else:
                j += 1

        return ans

1035 - Uncrossed Lines

leetcode

Problem

We write the integers of A and B (in the order they are given) on two separate horizontal lines.

Now, we may draw connecting lines: a straight line connecting two numbers A[i] and B[j] such that:

A[i] == B[j];
The line we draw does not intersect any other connecting (non-horizontal) line.
Note that a connecting lines cannot intersect even at the endpoints: each number can only belong to one connecting line.

Return the maximum number of connecting lines we can draw in this way.

Example 1:

Input: A = [1,4,2], B = [1,2,4]
Output: 2
Explanation: We can draw 2 uncrossed lines as in the diagram.
We cannot draw 3 uncrossed lines, because the line from A[1]=4 to B[2]=4 will intersect the line from A[2]=2 to B[1]=2.

Example 2:

Input: A = [2,5,1,2,5], B = [10,5,2,1,5,2]
Output: 3

Example 3:

Input: A = [1,3,7,1,7,5], B = [1,9,2,5,1]
Output: 2

Note:

1 <= A.length <= 500
1 <= B.length <= 500
1 <= A[i], B[i] <= 2000

Solution

class Solution:
    def maxUncrossedLines(self, A, B):

        dp = [[0] * (len(A)+1) for _ in range(len(B)+1)]

        for i in range(len(B)):
            for j in range(len(A)):
                if B[i] == A[j]:
                    dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1], dp[i][j] + 1)
                else:
                    dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1], dp[i][j])

        return dp[-1][-1]

886 - Possible Bipartition

leetcode

Problem

Given a set of N people (numbered 1, 2, ..., N), we would like to split everyone into two groups of any size.

Each person may dislike some other people, and they should not go into the same group.

Formally, if dislikes[i] = [a, b], it means it is not allowed to put the people numbered a and b into the same group.

Return true if and only if it is possible to split everyone into two groups in this way.

Example 1:

Input: N = 4, dislikes = [[1,2],[1,3],[2,4]]
Output: true
Explanation: group1 [1,4], group2 [2,3]

Example 2:

Input: N = 3, dislikes = [[1,2],[1,3],[2,3]]
Output: false

Example 3:

Input: N = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]]
Output: false


Note:

1. 1 <= N <= 2000
2. 0 <= dislikes.length <= 10000
3. 1 <= dislikes[i][j] <= N
4. dislikes[i][0] < dislikes[i][1]
5. There does not exist i != j for which dislikes[i] == dislikes[j].

Solution

Graph.

Every dislike is an edge in graph.

We color all vertice in two colors. Every adjacent vertice should have different colors.

Use DFS to color all vetices. If you can color all vertices without contradiction, then the graph has a bipartition.

class Solution:
    def possibleBipartition(self, N: int, dislikes):
        self.graph = [[] for _ in range(N+1)]
        self.marked = [0] * (N+1)

        for pair in dislikes:
            v, w = pair
            self.graph[v].append(w)
            self.graph[w].append(v)

        for v in range(1, N+1):
            if not self.marked[v] and not self.dfs(v, 1):
                return False
        return True

    def dfs(self, v, color):
        self.marked[v] = color
        for w in self.graph[v]:
            if not self.marked[w] and not self.dfs(w, -color):
                return False
            elif self.marked[w] == color:
                return False

        return True

207 - Course Schedule

leetcode

Problem

There are a total of numCourses courses you have to take, labeled from 0 to numCourses-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
             To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
             To take course 1 you should have finished course 0, and to take course 0 you should
             also have finished course 1. So it is impossible.

Solution

Solution 1: DAG with DFS

class Solution:
    def canFinish(self, numCourses, prerequisites):
        self.hasCycle = False
        self.adj = [[] for _ in range(numCourses)]
        self.marked = [0] * numCourses
        self.stack = [0] * numCourses

        for pre in prerequisite:
            v, w = pre
            adj[v].append(w)

        for v in range(numCourses):
            if not self.marked[v]:
                self.dfs(v)

        return not self.hasCycle

    def dfs(self, v):
        self.marked[v] = 1
        self.stack[v] = 1

        for w in self.adj[v]:
            if self.hasCycle:
                return
            elif not self.marked[w]:
                self.dfs(w)
            elif self.stack[w]:
                self.hasCycle = True

Solution 2: Topological Sort

If a graph has a topological sort order, there is no cycle.

class Solution:
    def canFinish(self, numCourses, prerequisites):
        adj = [[] for _ in range(numCourses)]
        degree = [0] * numCourses

        for pre in prerequisites:
            v, w = pre
            adj[w].append(v)
            degree[v] += 1

        q = [v for v in range(numCourses) if not degree[v]]
        order = []

        while q:
            v = q.pop(0)
            order.append(v)
            for w in adj[v]:
                degree[w] -= 1
                if not degree[w]:
                    q.append(w)

        return len(order) == numCourses

210 - Course Schedule II

leetcode

Problem

There are a total of n courses you have to take, labeled from 0 to n-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.

There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.

Example 1:

Input: 2, [[1,0]]
Output: [0,1]
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished
             course 0. So the correct course order is [0,1] .
Example 2:

Input: 4, [[1,0],[2,0],[3,1],[3,2]]
Output: [0,1,2,3] or [0,2,1,3]
Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both
             courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0.
             So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3] .

Solution

Topological sort.

class Solution:
    def canFinish(self, numCourses, prerequisites):
        adj = [[] for _ in range(numCourses)]
        degree = [0] * numCourses

        for pre in prerequisites:
            v, w = pre
            adj[w].append(v)
            degree[v] += 1

        q = [v for v in range(numCourses) if not degree[v]]
        order = []

        while q:
            v = q.pop(0)
            order.append(v)
            for w in adj[v]:
                degree[w] -= 1
                if not degree[w]:
                    q.append(w)

        return len(order) == numCourses

973 - K Closest Points to Origin

leetcode

Problem

We have a list of points on the plane.  Find the K closest points to the origin (0, 0).

(Here, the distance between two points on a plane is the Euclidean distance.)

You may return the answer in any order.  The answer is guaranteed to be unique (except for the order that it is in.)

Example 1:

Input: points = [[1,3],[-2,2]], K = 1
Output: [[-2,2]]
Explanation:
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest K = 1 points from the origin, so the answer is just [[-2,2]].

Example 2:

Input: points = [[3,3],[5,-1],[-2,4]], K = 2
Output: [[3,3],[-2,4]]
(The answer [[-2,4],[3,3]] would also be accepted.)

Solution

Solution 1: sort

class Solution:
    def kClosest(self, points, K: int):
        return sorted(points, key=lambda p: p[0]**2 + p[1]**2)[:K]

Solution 2: heap

class Solution:
    def kClosest(self, points, K):
        return heapq.nsmallest(K, points, lambda (x, y): x * x + y * y)

72 - Edit Distance

leetcode

Problem

Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.

You have the following 3 operations permitted on a word:

Insert a character
Delete a character
Replace a character

Example 1:

Input: word1 = "horse", word2 = "ros"
Output: 3

Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')

Example 2:

Input: word1 = "intention", word2 = "execution"
Output: 5

Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')

Solution

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        dp = [[0] * (1+len(word2)) for _ in range(1+len(word1))]

        for i in range(1+len(word1)):
            dp[i][0] = i
        for j in range(1+len(word2)):
            dp[0][j] = j

        for i in range(1, 1+len(word1)):
            for j in range(1, 1+len(word2)):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1

        return dp[-1][-1]

226 - Invert Binary Tree

leetcode

Problem

Invert a binary tree.

Example:

Input:

     4
   /   \
  2     7
 / \   / \
1   3 6   9
Output:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

Solution

class Solution:
    def invertTree(self):
        if root:
            tmp = root.left
            root.left = self.invertTree(root.right)
            root.right = self.invertTree(tmp)
        return root

746 - Min Cost Climbing Stairs

leetcode

Problem

On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed).

Once you pay the cost, you can either climb one or two steps. You need to find minimum cost to reach the top of the floor, and you can either start from the step with index 0, or the step with index 1.

Example 1:
Input: cost = [10, 15, 20]
Output: 15
Explanation: Cheapest is start on cost[1], pay that cost and go to the top.

Example 2:
Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
Output: 6
Explanation: Cheapest is start on cost[0], and only step on 1s, skipping cost[3].

Note:
cost will have a length in the range [2, 1000].
Every cost[i] will be an integer in the range [0, 999].

Solution

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        dp = [float('inf')] * len(cost)
        dp[0] = dp[1] = 0

        for i in range(2, len(cost)):
            dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])
        return min(dp[-1]+cost[-1], dp[-2]+cost[-2])

237 - Delete Node in a Linked List

leetcode

Problem

Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.

Given linked list -- head = [4,5,1,9], which looks like following:

Example 1:

Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list should become 4 -> 1 -> 9 after calling your function.

Example 2:

Input: head = [4,5,1,9], node = 1
Output: [4,5,9]
Explanation: You are given the third node with value 1, the linked list should become 4 -> 5 -> 9 after calling your function.

Solution

class Solution:
    def deleteNode(self, node):
        node.val = node.next.val
        node.next = node.next.next

1029 - Two City Scheduling

leetcode

Problem

There are 2N people a company is planning to interview. The cost of flying the i-th person to city A is costs[i][0], and the cost of flying the i-th person to city B is costs[i][1].

Return the minimum cost to fly every person to a city such that exactly N people arrive in each city.

Example 1:

Input: [[10,20],[30,200],[400,50],[30,20]]
Output: 110
Explanation:
The first person goes to city A for a cost of 10.
The second person goes to city A for a cost of 30.
The third person goes to city B for a cost of 50.
The fourth person goes to city B for a cost of 20.

The total minimum cost is 10 + 30 + 50 + 20 = 110 to have half the people interviewing in each city.

Note:

1 <= costs.length <= 100
It is guaranteed that costs.length is even.
1 <= costs[i][0], costs[i][1] <= 1000

Solution

Solution 1: DP

class Solution:
    def twoCitySchedCost(self, costs: List[List[int]]) -> int:
        N = len(costs) // 2

        dp = [[[float('inf')] * (N + 1), [float('inf')] * (N + 1)]
              for _ in range(2 * N + 1)]

        dp[0][0][0] = 0
        dp[0][1][0] = 0

        for i in range(1, 2 * N + 1):
            for k in range(1, N + 1):
                if i - k >= 0 and i - k <= N:
                    dp[i][0][k] = min(dp[i - 1][0][k - 1],
                                      dp[i - 1][1][i - k]) + costs[i - 1][0]
                    dp[i][1][k] = min(dp[i - 1][1][k - 1],
                                      dp[i - 1][0][i - k]) + costs[i - 1][1]

        return min(dp[-1][0][-1], dp[-1][1][-1])

Solution 2: sort

class Solution:
    def twoCitySchedCost(self, costs):
        sortedCosts = sorted(costs, key=lambda cost: cost[0] - cost[1])

        minCost = 0
        N = len(costs)

        for i in range(N//2):
            minCost += sortedCosts[i][0]
            minCost += sortedCosts[N//2+i][1]

        return minCost

528 - Random Pick with Weight

leetcode

Problem

Given an array w of positive integers, where w[i] describes the weight of index i, write a function pickIndex which randomly picks an index in proportion to its weight.

Note:

1 <= w.length <= 10000
1 <= w[i] <= 10^5
pickIndex will be called at most 10000 times.

Example 1:

Input:
["Solution","pickIndex"]
[[[1]],[]]
Output: [null,0]

Example 2:

Input:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
Output: [null,0,1,1,1,0]
Explanation of Input Syntax:

The input is two lists: the subroutines called and their arguments. Solution's constructor has one argument, the array w. pickIndex has no arguments. Arguments are always wrapped with a list, even if there aren't any.

Solution

class Solution:
    def __init__(self, w):
        self.w = list(itertools.accumulate(w))

    def pickIndex(self):
        return bisect.bisect_left(self.w, random.randint(1, self.w[-1]))

518 - Coin Change 2

leetcode

Problem

You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.

Example 1:

Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

Example 3:

Input: amount = 10, coins = [10]
Output: 1

Note:

You can assume that

0 <= amount <= 5000
1 <= coin <= 5000
the number of coins is less than 500
the answer is guaranteed to fit into signed 32-bit integer

Solution

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp = [0] * (amount + 1)
        dp[0] = 1

        for i in coins:
            for j in range(i, amount + 1):
                dp[j] += dp[j - i]

        return dp[-1]

231 - Power of Two

leetcode

Problem

Given an integer, write a function to determine if it is a power of two.

Example 1:

Input: 1
Output: true
Explanation: 20 = 1

Example 2:

Input: 16
Output: true
Explanation: 24 = 16

Example 3:

Input: 218
Output: false

Solution

Solution 1: Straight forward

class Solution:
    def powerOfTwo(self, n):
        while n != 1:
            if n % 2:
                return False
            n //= 2

        return True

Solution 2: Bit manipulation

power of two: n = 1000000 n - 1 = 111111 n & (n-1) == 0

Note: n can not be 0

class Solution:
    def powerOfTwo(self, n):
        return n and not (n & n-1)

406 - Queue Reconstruction by Height

leetcode

Problem

Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue.

Note:
The number of people is less than 1,100.

Example

Input:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

Output:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

Solution

class Solution:
    def main(self, people):
        people.sort(key=lambda p: (-p[0], p[1]))
        ans = []

        for p in people:
            ans.insert(p[1], p)

213 - House Robber II

leetcode

Problem

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [2,3,2]
Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2),
             because they are adjacent houses.
Example 2:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
             Total amount you can rob = 1 + 3 = 4.

Solution

Two loops of dp:

  1. consider the array from 0 to n - 2
  2. consider the array from 1 to n - 1
  3. return the max value from this two loops
class Solution:
    def rob(self, nums):
        if not nums:
            return 0
        if len(nums) == 1:
            return nums[0]

        rob = nrob = rob2 = nrob2 = 0

        for n in range(1, len(nums)):
            rob, nrob = nrob + nums[n], max(rob, nrob)
            rob2, nrob2 = nrob2 + nums[n - 1], max(rob2, nrob2)

        return max(rob, nrob, rob2, nrob2)

38 - Count and Say

leetcode

Problem

The count-and-say sequence is the sequence of integers with the first five terms as following:

1.     1
2.     11
3.     21
4.     1211
5.     111221
1 is read off as "one 1" or 11.
11 is read off as "two 1s" or 21.
21 is read off as "one 2, then one 1" or 1211.

Given an integer n where 1 ≤ n ≤ 30, generate the nth term of the count-and-say sequence. You can do so recursively, in other words from the previous member read off the digits, counting the number of digits in groups of the same digit.

Note: Each term of the sequence of integers will be represented as a string.



Example 1:

Input: 1
Output: "1"
Explanation: This is the base case.
Example 2:

Input: 4
Output: "1211"
Explanation: For n = 3 the term was "21" in which we have two groups "2" and "1", "2" can be read as "12" which means frequency = 1 and value = 2, the same way "1" is read as "11", so the answer is the concatenation of "12" and "11" which is "1211".

Solution

class Solution:
    def countAndSay(self, n: int) -> str:
        s = "1"

        for i in range(n-1):
            say = s[0]
            count = 0
            tmp = ""
            for c in s:
                if c == say:
                    count += 1
                else:
                    tmp = tmp + str(count) + say
                    say = c
                    count = 1
            s = tmp + str(count) + say
        return s

66 - Plus One

leetcode

Problem

Given a non-empty array of digits representing a non-negative integer, plus one to the integer.

The digits are stored such that the most significant digit is at the head of the list, and each element in the array contain a single digit.

You may assume the integer does not contain any leading zero, except the number 0 itself.

Example 1:

Input: [1,2,3]
Output: [1,2,4]
Explanation: The array represents the integer 123.
Example 2:

Input: [4,3,2,1]
Output: [4,3,2,2]
Explanation: The array represents the integer 4321.

Solution

class Solution:
    def plusOne(self, digits):
        add = 1

        for i in range(len(digits) - 1, -1, -1):
            digits[i], add = (digits[i] + add) % 10, (digits[i] + add)//10

        if add:
            digits.insert(0, add)

        return digits

392 - Is Subsequence

leetcode

Problem

Given a string s and a string t, check if s is subsequence of t.

A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ace" is a subsequence of "abcde" while "aec" is not).

Follow up:
If there are lots of incoming S, say S1, S2, ... , Sk where k >= 1B, and you want to check one by one to see if T has its subsequence. In this scenario, how would you change your code?

Credits:
Special thanks to @pbrother for adding this problem and creating all test cases.



Example 1:

Input: s = "abc", t = "ahbgdc"
Output: true
Example 2:

Input: s = "axc", t = "ahbgdc"
Output: false

Solution

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        i = 0
        for c in t:
            if i < len(s) and c == s[i]:
                i += 1
        return i == len(s)

40 - Combination Sum II

leetcode

Problem

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

Each number in candidates may only be used once in the combination.

Note:

All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:

Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]
Example 2:

Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
  [1,2,2],
  [5]
]

Solution

Backtracking problem.

Use DFS.

  1. make a choice
  2. call dfs recursive for all rest choices
  3. revert the choice

Notes:

  1. sort the candidates and avoid the duplicates
  2. return early when the target is smaller than 0
class Solution:
    def combinationSum(self, candidates, target):
        self.ans = []
        candidates.sort()
        self.dfs(candidates, 0, [], target)
        return self.ans

    def dfs(self, candidates, index, path, target):
        if target < 0:
            return

        if target == 0:
            self.ans.append(list(path))

        for i in range(index, len(candidates)):
            if i > index and candidates[i] == candidates[i-1]:
                continue
            path.append(candidates[i])
            self.dfs(i+1, candidates, path, target-candidates[i])
            path.pop()

39 - Combination Sum

leetcode

Problem

Given a set of candidate numbers (candidates) (without duplicates) and a target number (target),
find all unique combinations in candidates where the candidate numbers sums to target.

The same repeated number may be chosen from candidates unlimited number of times.

Note:

All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.

Example 1:

Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
  [7],
  [2,2,3]
]

Example 2:

Input: candidates = [2,3,5], target = 8,
A solution set is:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

Solution

Backtracking problem.

See Problem 40.

class Solution:
    def combinationSum(self, candidates, target):
        self.ans = []
        self.dfs(sorted(candidates), 0, [], target)
        return self.ans

    def dfs(self, candidates, index, path, target):
        if target == 0:
            self.ans.append(list(path))

        for i in range(index, len(candidates)):
            v = candidates[i]
            if v > target:
                return
            path.append(v)
            self.dfs(candidates, i, path, target-v)
            path.pop()

377 - Combination Sum IV

leetcode

Problem

Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

Example:

nums = [1, 2, 3]
target = 4

The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

Note that different sequences are counted as different combinations.

Therefore the output is 7.

Solution

It develops from DFS and then optimize it to DP problem

Consider you always choose a num as the leading number in the combination sequence.

Then for a target, the current combination would be:

for i in nums:
  dp[target] += dp[target - i]

Note here that we should have dp[target - i] already calculdated beforehand. So we need to do it from bottom-up. And the base case would be dp[0] = 1, because when i == target, we want dp[target - i] = 1 at first, it means we find the combination.

dp[0] = 1
for t in range(target+1):
  for i in nums:
    dp[target] += dp[target - i]

Solution 1: DFS

class Solution:
    def combinationSum4(self, nums, target):
        self.ans = 0
        self.dfs(nums, target)
        return self.ans

    def dfs(self, nums, target):
        if target < 0:
            return

        if target == 0:
            self.ans += 1

        for i in nums:
            self.dfs(nums, target - i)

It exceeds time limit.

Solution 2: DFS with memo

class Solution:
    def combinationSum4(self, nums, target):
        memo = [0] * (target+1)
        self.dfs(nums, target, memo)
        return memo[target]

    def dfs(self, nums, target, memo):
        if target < 0:
            return 0

        if memo[target]:
            return memo[target]

        if target == 0:
            return 1

        ans = 0
        for i in nums:
            ans += self.dfs(nums, target - i, memo)

        memo[target] = ans

        return ans

It still exceeds time limit.

Solution 3: DP

class Solution:
    def combinationSum4(self, nums, target):
        dp = [0] * (target + 1)
        dp[0] = 1

        for i in range(target+1):
            for j in nums:
                if i < j:
                    continue
                dp[i] += dp[i-j]

        return dp[-1]

216 - Combination Sum III

leetcode

Problem

Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.

Note:

All numbers will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:

Input: k = 3, n = 7
Output: [[1,2,4]]
Example 2:

Input: k = 3, n = 9
Output: [[1,2,6], [1,3,5], [2,3,4]]

Solution

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        ans = []
        self.dfs(1, [], ans, k, n)
        return ans


    def dfs(self, index, path, ans, k, n):
        if k == 0 and n == 0:
            ans.append(list(path))
            return

        for i in range(index, 10):
            if i > n:
                return
            path.append(i)
            self.dfs(i+1, path, ans, k-1, n-i)
            path.pop()

47 - Permutations II

leetcode

Problem

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

Example:

Input: [1,1,2]
Output:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

Solution

The tricky part is how to avoid duplicates.

First sort the array.

When chose the next element, if it is the same as the previous one and the previous one is now not choosen, we should skip it, because it means the previous one has been already choosen for this position before.

if i > 0 and nums[i] == nums[i-1] and not marked[i-1]:
  continue
class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        marked = [False] * len(nums)
        ans = []
        self.dfs(sorted(nums), marked, [], ans)
        return ans

    def dfs(self, nums, marked, path, ans):
        if len(path) == len(nums):
            ans.append(list(path))
            return

        for i in range(len(nums)):
            if i > 0 and nums[i] == nums[i-1] and not marked[i-1]:
                continue
            if marked[i]:
                continue
            marked[i] = True
            path.append(nums[i])
            self.dfs(nums, marked, path, ans)
            marked[i] = False
            path.pop()

51 - N-Queens

leetcode

Problem

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

Solution

class Solution:
    def solveNQueens(self, n):
        ans = []

        self.dfs(0, n, [], ans)
        return ans

    def dfs(self, row, n, path, ans):
        if row == n and len(path) == n:
            ans.append(self.draw(path, n))
        for i in range(n):
            if self.attack(path, row, i):
                continue
            path.append([row, i])
            self.dfs(row+1, n, path, ans)
            path.pop()

    def attack(self, path, row, i):
        for pair in path:
            if pair[1] == i:
                return True
            elif abs(row - pair[0]) == abs(i - pair[1]):
                return True
        return False

    def draw(self, path, n):
        ans = []
        for pair in path:
            ans.append("." * pair[1] + "Q" + "." * (n-1-pair[1]))

        return ans

52 - N-Queens II

leetcode

Problem

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return the number of distinct solutions to the n-queens puzzle.

Solution

class Solution:
    def totalNQueens(self, n):
        self.ans = 0
        self.dfs(0, n, [])
        return self.ans

    def dfs(self, row, n, path):
        if row == n and len(path) == n:
            self.ans += 1
        for i in range(n):
            if self.attack(path, row, i):
                continue
            path.append([row, i])
            self.dfs(row+1, n, path)
            path.pop()

    def attack(self, path, row, i):
        for pair in path:
            if pair[1] == i:
                return True
            elif abs(row - pair[0]) == abs(i - pair[1]):
                return True
        return False

77 - Combinations

leetcode

Problem

Share
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

Example:

Input: n = 4, k = 2
Output:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

Solution

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        self.dfs(1, n, k, [], ans)
        return ans

    def dfs(self, index, n, k, path, ans):
        if k == 0:
            ans.append(list(path))
            return

        for i in range(index, n+1):
            path.append(i)
            self.dfs(i+1, n, k-1, path, ans)
            path.pop()

90 - Subsets II

leetcode

Problem

Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: [1,2,2]
Output:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

Solution

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        ans = []
        marked = [False] * len(nums)

        self.dfs(sorted(nums), 0, [], ans, marked)

        return ans

    def dfs(self, nums, index, path, ans, marked):
        ans.append(list(path))
        for i in range(index, len(nums)):
            if i > 0 and nums[i] == nums[i-1] and not marked[i-1]:
                continue

            marked[i] = True
            path.append(nums[i])
            self.dfs(nums, i+1, path, ans, marked)
            path.pop()
            marked[i] = False

100 - Same Tree

leetcode

Problem

Given two binary trees, write a function to check if they are the same or not.

Two binary trees are considered the same if they are structurally identical and the nodes have the same value.

Example 1:

Input:     1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

Output: true
Example 2:

Input:     1         1
          /           \
         2             2

        [1,2],     [1,null,2]

Output: false
Example 3:

Input:     1         1
          / \       / \
         2   1     1   2

        [1,2,1],   [1,1,2]

Output: false

Solution

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if p and not q:
            return False
        if not p and q:
            return False
        if not p and not q:
            return True

        if p.val == q.val:
            return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
        else:
            return False

112 - Path Sum

leetcode

Problem

Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.

Note: A leaf is a node with no children.

Example:

Given the below binary tree and sum = 22,

      5
     / \
    4   8
   /   / \
  11  13  4
 /  \      \
7    2      1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False

        if sum - root.val == 0 and not root.right and not root.left:
            return True
        else:
            return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum-root.val)

113 - Path Sum II

leetcode

Problem

Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.

Note: A leaf is a node with no children.

Example:

Given the below binary tree and sum = 22,

      5
     / \
    4   8
   /   / \
  11  13  4
 /  \    / \
7    2  5   1
Return:

[
   [5,4,11,2],
   [5,8,4,5]
]

Solution

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:

        ans = []
        self.dfs(root, sum, [], ans)
        return ans

    def dfs(self, root, sum, path, ans):

        if not root:
            return

        path.append(root.val)
        if root.val == sum and not root.right and not root.left:
            ans.append(list(path))
        self.dfs(root.left, sum - root.val, path, ans)
        self.dfs(root.right, sum - root.val, path, ans)
        path.pop()

118 - Pascal’s Triangle

leetcode

Problem

Given a non-negative integer numRows, generate the first numRows of Pascal's triangle.

Example:

Input: 5
Output:
[
     [1],
    [1,1],
   [1,2,1],
  [1,3,3,1],
 [1,4,6,4,1]
]

Solution

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        ans = []

        for i in range(1, numRows+1):
            level = [1] * i

            if ans:
                for j in range(1, i-1):
                    level[j] = ans[-1][j-1] + ans[-1][j]

            ans.append(level)

        return ans

54 - Spiral Matrix

leetcode

Problem

Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.

Example 1:

Input:
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
Output: [1,2,3,6,9,8,7,4,5]
Example 2:

Input:
[
  [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

Clockwise turn with help of step and direction array

step = [[0, 1], [1, 0], [0, -1], [-1, 0]]
di = 0

i, i = i + step[di][0], j + step[di][1]

Clockwise Turn:

di = (di + 1) % 4
i, j = i+step[di][0], j + step[di][1]
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix:
            return []
        i = j = 0
        m = len(matrix)
        n = len(matrix[0])
        marked = [[False] * n for _ in matrix]
        step = [[0, 1], [1, 0], [0, -1], [-1, 0]]
        i = j = di = 0
        ans = []
        for _ in range(m*n):
            ans.append(matrix[i][j])
            marked[i][j] = True
            ni, nj = i + step[di][0], j + step[di][1]

            if 0 <= ni < m and 0 <= nj < n and not marked[ni][nj]:
                i, j = ni, nj
            else:
                di = (di + 1) % 4
                i, j = i+step[di][0], j + step[di][1]

        return ans

58 - Length of Last Word

leetcode

Problem

Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the length of last word (last word means the last appearing word if we loop from left to right) in the string.

If the last word does not exist, return 0.

Note: A word is defined as a maximal substring consisting of non-space characters only.

Example:

Input: "Hello World"
Output: 5

Solution

class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        if s.split():
            return len(s.split()[-1])
        else:
            return 0

59 - Spiral Matrix II

leetcode

Problem

Given a positive integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.

Example:

Input: 3
Output:
[
 [ 1, 2, 3 ],
 [ 8, 9, 4 ],
 [ 7, 6, 5 ]
]

Solution

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        matrix = [[0] * n for _ in range(n)]
        step = [[0,1], [1, 0], [0, -1], [-1, 0]]
        i = j = di = 0

        for x in range(1, n * n+1):
            matrix[i][j] = x

            ni = i + step[di][0]
            nj = j + step[di][1]

            if 0 <= ni < n and 0 <= nj < n and not matrix[ni][nj]:
                i, j = ni, nj
            else:
                di = (di + 1) % 4
                i, j = i + step[di][0], j + step[di][1]

        return matrix

61 - Rotate List

leetcode

Problem

Given a linked list, rotate the list to the right by k places, where k is non-negative.

Example 1:

Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL

Example 2:

Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL

Solution

Note: k may be larger than the total count of nodes. We sould preprocess k first

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
        if not head or not k:
            return head

        first = second = head

        count = 0

        while first:
            count += 1
            first = first.next

        k = k % count
        first = head

        for i in range(k):
            first = first.next

        while first.next:
            first = first.next
            second = second.next

        first.next = head
        head = second.next
        second.next = None

        return head

63 - Unique Paths II

leetcode

Problem

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

Note: m and n will be at most 100.

Example 1:

Input:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right

Solution

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        if not obstacleGrid:
            return 0

        m, n = len(obstacleGrid), len(obstacleGrid[0])

        dp = [[0] * (n+1) for _ in range(m+1)]

        dp[0][1] = 1

        for i in range(m):
            for j in range(n):
                if not obstacleGrid[i][j]:
                    dp[i+1][j+1] = dp[i+1][j] + dp[i][j+1]

        return dp[-1][-1]

74 - Search a 2D Matrix

leetcode

Problem

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

Integers in each row are sorted from left to right.
The first integer of each row is greater than the last integer of the previous row.
Example 1:

Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 3
Output: true
Example 2:

Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 13
Output: false

Solution

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        if not matrix or not matrix[0]:
            return False

        rl, rr = 0, len(matrix) - 1
        cl, cr = 0, len(matrix[0]) - 1

        while rl <= rr:
            mid = (rl + rr) // 2
            if target < matrix[mid][0]:
                rr = mid - 1
            elif target > matrix[mid][-1]:
                rl = mid + 1
            else:
                rl = mid
                break

        if rl > rr:
            return False

        while cl <= cr:
            mid = (cl + cr) // 2

            if target < matrix[rl][mid]:
                cr = mid - 1
            elif target > matrix[rl][mid]:
                cl = mid + 1
            else:
                return True

        return False

162 - Find Peak Element

leetcode

Problem

A peak element is an element that is greater than its neighbors.

Given an input array nums, where nums[i] ≠ nums[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

You may imagine that nums[-1] = nums[n] = -∞.

Example 1:

Input: nums = [1,2,3,1]
Output: 2
Explanation: 3 is a peak element and your function should return the index number 2.
Example 2:

Input: nums = [1,2,1,3,5,6,4]
Output: 1 or 5
Explanation: Your function can return either index number 1 where the peak element is 2,
             or index number 5 where the peak element is 6.

Solution

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        if len(nums) == 1:
            return 0
        if len(nums) == 2:
            return 0 if nums[0] > nums[1] else 1

        l = 1
        r = len(nums) - 2

        while l <= r:
            mid = (r + l) // 2
            if nums[mid-1] < nums[mid] > nums[mid+1]:
                return mid
            elif nums[mid] <= nums[mid+1]:
                l = mid + 1
            else:
                r = mid - 1

        if nums[0] > nums[1]:
            return 0
        elif nums[-1] > nums[-2]:
            return len(nums) - 1

83 - Remove Duplicates from Sorted List

leetcode

Problem

Given a sorted linked list, delete all duplicates such that each element appear only once.

Example 1:

Input: 1->1->2
Output: 1->2
Example 2:

Input: 1->1->2->3->3
Output: 1->2->3

Solution

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head

        first = head
        second = first.next

        while second:
            if first.val == second.val:
                first.next = second.next
                second = first.next
            else:
                first, second = second, second.next

        return head

95 - Unique Binary Search Trees II

leetcode

Problem

Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1 ... n.

Example:

Input: 3
Output:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]
Explanation:
The above output corresponds to the 5 unique BST's shown below:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

Solution

class Solution:
    def generateTrees(self, n: int) -> List[TreeNode]:
        if n == 0:
            return []
        return self.helper(1, n+1)

    def helper(self, start, end):
        if start >= end:
            return [None]
        res = []
        for i in range(start, end):
            for left in self.helper(start, i):
                for right in self.helper(i+1, end):
                    node = TreeNode(i)
                    node.left, node.right = left, right
                    res.append(node)
        return res

222 - Count Complete Tree Nodes

leetcode

Problem

Given a complete binary tree, count the number of nodes.

Note:

Definition of a complete binary tree from Wikipedia:
In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.

Example:

Input:
    1
   / \
  2   3
 / \  /
4  5 6

Output: 6

Solution

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if not root:
            return 0

        rd = self.height(root.right)
        ld = self.height(root.left)

        if rd == ld:
            return pow(2, ld) + self.countNodes(root.right)
        else:
            return pow(2, rd) + self.countNodes(root.left)

    def height(self, root):
        if not root:
            return 0

        return 1 + self.height(root.left)

130 - Surrounded Regions

leetcode

Problem

Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'.

A region is captured by flipping all 'O's into 'X's in that surrounded region.

Example:

X X X X
X O O X
X X O X
X O X X
After running your function, the board should be:

X X X X
X X X X
X X X X
X O X X
Explanation:

Surrounded regions shouldn’t be on the border, which means that any 'O' on the border of the board are not flipped to 'X'.
Any 'O' that is not on the border and it is not connected to an 'O' on the border will be flipped to 'X'.
Two cells are connected if they are adjacent cells connected horizontally or vertically.

Solution

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        if not board or not board[0]:
            return

        marked = [[False] * len(board[0]) for _ in board]

        for i in [0, len(board)-1]:
            for j in range(len(board[0])):
                if board[i][j] == 'O':
                    self.color(board, i, j, 'O', 'S')

        for i in range(1, len(board)-1):
            for j in [0, len(board[0])-1]:
                if board[i][j] == 'O':
                    self.color(board, i, j, 'O', 'S')

        for i in range(len(board)):
            for j in range(len(board[0])):
                board[i][j] = 'OX'[board[i][j] !='S']


    def color(self, board, i, j, target, symbol):
        if i >= len(board) or j >= len(board[0]) or i < 0 or j < 0:
            return

        if board[i][j] == target:
            board[i][j] = symbol
            for v, w in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
                self.color(board, i+v, j+w, target, symbol)

129 - Sum Root to Leaf Numbers

leetcode

Problem

Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.

An example is the root-to-leaf path 1->2->3 which represents the number 123.

Find the total sum of all root-to-leaf numbers.

Note: A leaf is a node with no children.

Example:

Input: [1,2,3]
    1
   / \
  2   3
Output: 25
Explanation:
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Therefore, sum = 12 + 13 = 25.
Example 2:

Input: [4,9,0,5,1]
    4
   / \
  9   0
 / \
5   1
Output: 1026
Explanation:
The root-to-leaf path 4->9->5 represents the number 495.
The root-to-leaf path 4->9->1 represents the number 491.
The root-to-leaf path 4->0 represents the number 40.
Therefore, sum = 495 + 491 + 40 = 1026.

Solution

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sumNumbers(self, root: TreeNode) -> int:
        if not root:
            return 0

        self.ans = 0
        self.dfs(root, [])
        return self.ans

    def dfs(self, root, path):
        if not root:
            return

        if not root.right and not root.left:
            path.append(root.val)
            self.ans += int("".join([str(i) for i in path]))
            path.pop()
            return

        path.append(root.val)
        self.dfs(root.left, path)
        self.dfs(root.right, path)
        path.pop()

988 - Smallest String Starting From Leaf

leetcode

Problem

Given the root of a binary tree, each node has a value from 0 to 25 representing the letters 'a' to 'z': a value of 0 represents 'a', a value of 1 represents 'b', and so on.

Find the lexicographically smallest string that starts at a leaf of this tree and ends at the root.

(As a reminder, any shorter prefix of a string is lexicographically smaller: for example, "ab" is lexicographically smaller than "aba".  A leaf of a node is a node that has no children.

Example 1:

Input: [0,1,2,3,4,3,4]
Output: "dba"

Example 2:

Input: [25,1,3,1,3,0,2]
Output: "adz"

Solution

class Solution:
    def smallestFromLeaf(self, root: TreeNode) -> str:
        self.ans = "~"
        self.dfs(root, [])
        return self.ans

    def dfs(self, root, path):

        if not root:
            return

        path.append(chr(root.val+97))

        if not root.left and not root.right:
            self.ans = min(self.ans, "".join(reversed(path)))

        self.dfs(root.left, path)
        self.dfs(root.right, path)
        path.pop()

303 - Range Sum Query - Immutable

leetcode

Problem

Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.

Example:
Given nums = [-2, 0, 3, -5, 2, -1]

sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
Note:
You may assume that the array does not change.
There are many calls to sumRange function.

Solution

class NumArray:
    def __init__(self, nums: List[int]):
        self.dp = list(nums)
        self.dp.insert(0, 0)
        for i in range(1, len(nums)):
            self.dp[i+1] += self.dp[i]

    def sumRange(self, i: int, j: int) -> int:
        return self.dp[j+1] - self.dp[i]

264 - Ugly Number II

leetcode

Problem

Write a program to find the n-th ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.

Example:

Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.
Note:

1 is typically treated as an ugly number.
n does not exceed 1690.

Solution

class Solution:
    ugly = sorted(2**a * 3**b * 5**c for a in range(32) for b in range(32) for c in range(32))
    def nthUglyNumber(self, n: int) -> int:
        return self.ugly[n-1]

263 - Ugly Number

leetcode

Problem

Write a program to check whether a given number is an ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.

Example 1:

Input: 6
Output: true
Explanation: 6 = 2 × 3

Example 2:

Input: 8
Output: true
Explanation: 8 = 2 × 2 × 2

Example 3:

Input: 14
Output: false
Explanation: 14 is not ugly since it includes another prime factor 7.
Note:

1 is typically treated as an ugly number.
Input is within the 32-bit signed integer range: [−231,  231 − 1].

Solution

class Solution:
    def isUgly(self, num: int) -> bool:
        if num <= 0:
            return False

        while num != 1:
            tmp = num
            for n in [2, 3, 5]:
                if num % n == 0:
                    num //= n
            if tmp == num:
                return False
        return True

332 - Reconstruct Itinerary

leetcode

Problem

Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the itinerary in order. All of the tickets belong to a man who departs from JFK. Thus, the itinerary must begin with JFK.

Note:

If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string. For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"].
All airports are represented by three capital letters (IATA code).
You may assume all tickets form at least one valid itinerary.
One must use all the tickets once and only once.

Example 1:

Input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
Output: ["JFK", "MUC", "LHR", "SFO", "SJC"]

Example 2:

Input: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
Output: ["JFK","ATL","JFK","SFO","ATL","SFO"]
Explanation: Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"].
             But it is larger in lexical order.

Solution

Eulerian Path: a path in graph that visits every edge exactly once.

Eulerian Circuit is an Eulerian Path which starts and ends on the same vertex.

Hierholzer’s algorithm:

Modify the graph dfs to backtrack the vertices if there is no other unvisited edges of that vertice.

while edges[vertice]:
    dfs(edges[vertice].pop())
path.append(vertice)

For this problem, we have to notice the lexical order for choices during dfs.

class Solution:
    def findItinerary(self, tickets: List[List[str]]) -> List[str]:
        edges = defaultdict(list)

        for v, w in sorted(tickets)[::-1]:
            edges[v] += [w]
        path = []
        self.dfs("JFK", edges, path)
        return path[::-1]

    def dfs(self, vertice, edges, path):
        while(edges[vertice]):
            self.dfs(edges[vertice].pop(), edges, path)
        path.append(vertice)

212 - Word Search II

leetcode

Problem

Given a 2D board and a list of words from the dictionary, find all words in the board.

Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

Example:

Input:
board = [
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
words = ["oath","pea","eat","rain"]

Output: ["eat","oath"]


Note:

All inputs are consist of lowercase letters a-z.
The values of words are distinct.

Solution

DFS the matrix.

To break the dfs earlier, a Trie data structure is introduced to check wether the path is a valid combination

class TrieNode:
    def __init__(self):
        self.children = defaultdict(TrieNode)
        self.isWord = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        node = self.root
        for w in word:
            node = node.children[w]
        node.isWord = True

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        trie = Trie()
        node = trie.root
        for w in words:
            trie.insert(w)
        ans = []
        for i in range(len(board)):
            for j in range(len(board[0])):
                self.dfs(board, i, j, node, "", ans)
        return ans


    def dfs(self, board, i, j, node, path, ans):
        if node.isWord:
            ans.append(path)
            node.isWord = False

        if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or board[i][j] not in node.children:
            return

        val = board[i][j]
        board[i][j] = "#"
        for pair in [[0, 1], [1, 0], [0, -1], [-1, 0]]:
            self.dfs(board, i+pair[0], j+pair[1], node.children[val], path+val, ans)
        board[i][j] = val

441 - Arranging Coins

leetcode

Problem

You have a total of n coins that you want to form in a staircase shape, where every k-th row must have exactly k coins.

Given n, find the total number of full staircase rows that can be formed.

n is a non-negative integer and fits within the range of a 32-bit signed integer.

Example 1:

n = 5

The coins can form the following rows:
¤
¤ ¤
¤ ¤

Because the 3rd row is incomplete, we return 2.
Example 2:

n = 8

The coins can form the following rows:
¤
¤ ¤
¤ ¤ ¤
¤ ¤

Because the 4th row is incomplete, we return 3.

Solution

class Solution:
    def arrangeCoins(self, n: int) -> int:
        k = 0
        while n > 0:
            k += 1
            n -= k

        return k if not n else k - 1

107 - Binary Tree Level Order Traversal II

leetcode

Problem

Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root).

For example:
Given binary tree [3,9,20,null,null,15,7],
    3
   / \
  9  20
    /  \
   15   7
return its bottom-up level order traversal as:
[
  [15,7],
  [9,20],
  [3]
]

Solution

class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        queue = [root]
        stack = []
        while queue:
            next_queue = []
            tmp = []
            while queue:
                n = queue.pop(0)
                tmp.append(n.val)
                if n.left:
                    next_queue.append(n.left)
                if n.right:
                    next_queue.append(n.right)
            stack.append(tmp[:])
            queue = next_queue[:]

        return stack[::-1]

463 - Island Perimeter

leetcode

Problem

You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water.

Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells).

The island doesn't have "lakes" (water inside that isn't connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don't exceed 100. Determine the perimeter of the island.

Solution

class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        if not grid or not grid[0]:
            return 0

        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]:
                    return self.dfs(grid, i, j)
        return 0

    def dfs(self, grid, i, j):
        if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]):
            return 1

        if grid[i][j] == -1:
            return 0

        if grid[i][j]:
            grid[i][j] = -1
            return self.dfs(grid, i + 1, j) + self.dfs(
                grid, i, j + 1) + self.dfs(grid, i - 1, j) + self.dfs(
                    grid, i, j - 1)
        else:
            return 1

695 - Max Area of Island

leetcode

Problem

Given a non-empty 2D array grid of 0's and 1's, an island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.

Find the maximum area of an island in the given 2D array. (If there is no island, the maximum area is 0.)

Example 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
 [0,0,0,0,0,0,0,1,1,1,0,0,0],
 [0,1,1,0,1,0,0,0,0,0,0,0,0],
 [0,1,0,0,1,1,0,0,1,0,1,0,0],
 [0,1,0,0,1,1,0,0,1,1,1,0,0],
 [0,0,0,0,0,0,0,0,0,0,1,0,0],
 [0,0,0,0,0,0,0,1,1,1,0,0,0],
 [0,0,0,0,0,0,0,1,1,0,0,0,0]]
Given the above grid, return 6. Note the answer is not 11, because the island must be connected 4-directionally.

Example 2:

[[0,0,0,0,0,0,0,0]]
Given the above grid, return 0.
Note: The length of each dimension in the given grid does not exceed 50.

Solution

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:

        if not grid or not grid[0]:
            return 0

        ans = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] > 0:
                    ans = max(ans, self.dfs(grid, i, j))
        return ans

    def dfs(self, grid, i, j):

        if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or not grid[i][j]:
            return 0

        grid[i][j] = 0
        count = 1
        count += self.dfs(grid, i+1, j)
        count += self.dfs(grid, i-1, j)
        count += self.dfs(grid, i, j+1)
        count += self.dfs(grid, i, j-1)
        return count

15 - 3SUM

leetcode

Problem

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:

The solution set must not contain duplicate triplets.

Example:

Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

Solution

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if len(nums) < 3:
            return []
        ans = []
        nums.sort()
        for i in range(0, len(nums)-2):
            if nums[i] > 0:
                break
            if i > 0 and nums[i-1] == nums[i]:
                continue
            left, right = i+1, len(nums)-1

            while right > left:
                s = nums[left] + nums[right] + nums[i]
                if s == 0:
                    ans.append([nums[i], nums[left], nums[right]])
                    left += 1
                    right -= 1
                    while right > left and nums[left] == nums[left-1]:
                        left += 1
                    while right > left and nums[right] == nums[right+1]:
                        right -= 1
                elif s < 0:
                    left += 1
                else:
                    right -= 1
        return ans

10 - Regular Expression Matching

leetcode

Problem

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).

Note:

s could be empty and contains only lowercase letters a-z.
p could be empty and contains only lowercase letters a-z, and characters like . or *.
Example 1:

Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

Example 2:

Input:
s = "aa"
p = "a*"
Output: true
Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".

Example 4:

Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".

Example 5:

Input:
s = "mississippi"
p = "mis*is*p*."
Output: false

Solution

class Solution:
    def isMatch(self, s: str, p: str) -> bool:

        dp = [[False] * (len(s)+1) for _ in range(len(p)+1)]

        dp[0][0] = True

        for i in range(len(p)):
            if p[i] == "*" and dp[i-1][0]:
                dp[i+1][0] = True

        for i in range(len(p)):
            for j in range(len(s)):
                if p[i] == s[j]:
                    dp[i+1][j+1] = dp[i][j]
                elif p[i] == '.':
                    dp[i+1][j+1] = dp[i][j]
                elif p[i] == '*':
                    if p[i-1] != s[j] and p[i-1] != '.':
                        dp[i+1][j+1] = dp[i-1][j+1]
                    else:
                        dp[i+1][j+1] = dp[i][j+1] or dp[i+1][j] or dp[i-1][j+1]
        return dp[-1][-1]

37 - Sudoku Solver

leetcode

Problem

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

Each of the digits 1-9 must occur exactly once in each row.
Each of the digits 1-9 must occur exactly once in each column.
Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.
Empty cells are indicated by the character '.'.

Note:

The given board contain only digits 1-9 and the character '.'.
You may assume that the given Sudoku puzzle will have a single unique solution.
The given board size is always 9x9.

Solution

class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        self.finish = False
        self.solve(board, 0, 0)

    def solve(self, board, i, j):
        if i >= len(board):
            self.finish = True
            return
        next_i, next_j = i + (j + 1) // 9, (j + 1) % 9
        if board[i][j] == '.':
            for n in range(1, 10):
                if self.is_safe(board, i, j, str(n)):
                    board[i][j] = str(n)
                    self.solve(board, next_i, next_j)
                    if self.finish:
                        return
                    board[i][j] = '.'
        else:
            self.solve(board, next_i, next_j)

    def is_safe(self, board, i, j, n):
        return self.is_safe_vertical(
            board, i, j, n) and self.is_safe_horizontal(
                board, i, j, n) and self.is_safe_sub(board, i, j, n)

    def is_safe_vertical(self, board, i, j, n):
        return n not in [a[j] for a in board]

    def is_safe_horizontal(self, board, i, j, n):
        return n not in board[i]

    def is_safe_sub(self, board, i, j, n):
        p1 = i // 3 * 3
        p2 = j // 3 * 3
        for x in range(p1, p1 + 3):
            for y in range(p2, p2 + 3):
                if board[x][y] == n:
                    return False
        return True

36 - Valid Sudoku

leetcode

Problem

Determine if a 9x9 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 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition.

Solution

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        for row in board:
            nums = [x for x in row if x != '.' ]
            if self.notValid(nums):
                return False

        for i in range(len(board[0])):
            col = [row[i] for row in board]
            nums = [x for x in col if x != '.' ]
            if self.notValid(nums):
                return False

        for i in [0, 3, 6]:
            for j in [0, 3, 6]:
                nums = [x for row in board[i:i+3] for x in row[j:j+3] if x != '.']
                if self.notValid(nums):
                    return False
        return True

    def notValid(self, nums):
        return len(set(nums)) != len(nums)

190 - Reverse Bits

leetcode

Problem

Reverse bits of a given 32 bits unsigned integer.

Example 1:

Input: 00000010100101000001111010011100
Output: 00111001011110000010100101000000
Explanation: The input binary string 00000010100101000001111010011100 represents the unsigned integer 43261596, so return 964176192 which its binary representation is 00111001011110000010100101000000.

Example 2:

Input: 11111111111111111111111111111101
Output: 10111111111111111111111111111111
Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 3221225471 which its binary representation is 10111111111111111111111111111111.

Note:

Note that in some languages such as Java, there is no unsigned integer type. In this case, both input and output will be given as signed integer type and should not affect your implementation, as the internal binary representation of the integer is the same whether it is signed or unsigned.
In Java, the compiler represents the signed integers using 2's complement notation. Therefore, in Example 2 above the input represents the signed integer -3 and the output represents the signed integer -1073741825.

Solution

Solution 1: Reverse bit one by one

class Solution:
    def reverseBits(self, n: int) -> int:
        res = 0
        shift = 31
        while n:
            res += (n & 1) << shift
            n = n >> 1
            shift -= 1
        return res

Solution 2: Reverse bit with masks

  1. First we swith first 16 bits and last 16 bits
  2. Repeat this operation in each half part. For example switch the first 8 bits and last 8 bits in the left 16 bits. Do the same in the right one
  3. Until only switch one bit
class Solution:
    def reverseBits(self, n: int) -> int:
        n = (n << 16 | n >> 16)
        n = ((n & 0xff00ff00) >> 8 | (n & 0x00ff00ff) << 8)
        n = ((n & 0xf0f0f0f0) >> 4 | (n & 0x0f0f0f0f) << 4)
        n = ((n & 0xcccccccc) >> 2 | (n & 0x33333333) << 2)
        n = ((n & 0xaaaaaaaa) >> 1 | (n & 0x55555555) << 1)
        return n

461 - Hamming Distance

leetcode

Problem

The Hamming distance between two integers is the number of positions at which the corresponding bits are different.

Given two integers x and y, calculate the Hamming distance.

Note:
0 ≤ x, y < 231.

Example:

Input: x = 1, y = 4

Output: 2

Explanation:
1   (0 0 0 1)
4   (0 1 0 0)
       ↑   ↑

The above arrows point to positions where the corresponding bits are different.
Accepted

Solution

Solution 1:

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        z = x ^ y
        s = 0
        while z:
            s += z & 1
            z = z >> 1
        return s

Solution 2: t & (t - 1)

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        z = x ^ y
        s = 0
        while z:

            s += 1
            z = z & (z - 1)

        return s

32 - Longest Valid Parentheses

leetcode

Problem

Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.

Example 1:

Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"

Example 2:

Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"

Solution

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        stack = [-1]
        ans = 0
        for i in range(len(s)):
            if s[i] == '(':
                stack.append(i)
            else:
                if stack:
                    stack.pop()
                if stack:
                    ans = max(ans, i - stack[-1])
                else:
                    stack.append(i)
        return ans

71 - Simplify Path

leetcode

Problem

Given an absolute path for a file (Unix-style), simplify it. Or in other words, convert it to the canonical path.

In a UNIX-style file system, a period . refers to the current directory. Furthermore, a double period .. moves the directory up a level.

Note that the returned canonical path must always begin with a slash /, and there must be only a single slash / between two directory names. The last directory name (if it exists) must not end with a trailing /. Also, the canonical path must be the shortest string representing the absolute path.

Example 1:

Input: "/home/"
Output: "/home"
Explanation: Note that there is no trailing slash after the last directory name.

Example 2:

Input: "/../"
Output: "/"
Explanation: Going one level up from the root directory is a no-op, as the root level is the highest level you can go.

Example 3:

Input: "/home//foo/"
Output: "/home/foo"
Explanation: In the canonical path, multiple consecutive slashes are replaced by a single one.

Example 4:

Input: "/a/./b/../../c/"
Output: "/c"

Example 5:

Input: "/a/../../b/../c//.//"
Output: "/c"

Example 6:

Input: "/a//b////c/d//././/.."
Output: "/a/b/c"

Solution

class Solution:
    def simplifyPath(self, path: str) -> str:
        stack = []
        for p in path.split('/'):
            if p == '..':
                if stack:
                    stack.pop()
            elif p and p != '.':
                stack.append(p)

        return '/' + "/".join(stack)

73 - Set Matrix Zeroes

leetcode

Problem

Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in-place.

Example 1:

Input:
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]
Output:
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]

Example 2:

Input:
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]
Output:
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]
Follow up:

A straight forward solution using O(mn) space is probably a bad idea.
A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?

Solution

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        col_zero = False

        for i in range(len(matrix)):
            if matrix[i][0] == 0:
                col_zero = True
            for j in range(1, len(matrix[0])):
                if matrix[i][j] == 0:
                    matrix[0][j] = matrix[i][0] = 0

        for i in range(1, len(matrix)):
            for j in range(1, len(matrix[0])):
                if not matrix[0][j] or not matrix[i][0]:
                    matrix[i][j] = 0

        if matrix[0][0] == 0:
            for j in range(len(matrix[0])):
                matrix[0][j] = 0

        if col_zero:
            for i in range(len(matrix)):
                matrix[i][0] = 0

80 - Remove Duplicates from Sorted Array II

leetcode

Problem

Given a sorted array nums, remove the duplicates in-place such that duplicates appeared at most twice and return the new length.

Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.

Example 1:

Given nums = [1,1,1,2,2,3],

Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3 respectively.

It doesn't matter what you leave beyond the returned length.
Example 2:

Given nums = [0,0,1,1,1,1,2,3,3],

Your function should return length = 7, with the first seven elements of nums being modified to 0, 0, 1, 1, 2, 3 and 3 respectively.

It doesn't matter what values are set beyond the returned length.

Solution

Keep a pointer to copy the value from behind.

Very interesting solution: n > nums[i-k], where k = 2

class Solution:
    def removeDuplicates(self, nums):
        i = 0
        k = 2
        for n in nums:
            if i < k or n > nums[i-k]:
                nums[i] = n
                i += 1
        return i

81 - Search in Rotated Sorted Array II

leetcode

Problem

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,0,1,2,2,5,6] might become [2,5,6,0,0,1,2]).

You are given a target value to search. If found in the array return true, otherwise return false.

Example 1:

Input: nums = [2,5,6,0,0,1,2], target = 0
Output: true
Example 2:

Input: nums = [2,5,6,0,0,1,2], target = 3
Output: false
Follow up:

This is a follow up problem to Search in Rotated Sorted Array, where nums may contain duplicates.
Would this affect the run-time complexity? How and why?

Solution

Note:

Consider the problem in two part:

  1. when the left part is in order
  2. when the right part is in order

Important: How to guarentee the left part is in order when there is duplicates in the array: We can use a while to eliminate the duplicates if nums[left] == nums[mid]

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        if not nums:
            return False

        left = 0
        right = len(nums) - 1

        while left < right:
            mid = (left + right) // 2

            if nums[mid] == target:
                return True

            while left < mid and nums[left] == nums[mid]:
                left += 1

            if nums[mid] >= nums[left]:
                if nums[mid] > target >= nums[left]:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if nums[mid] < target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid - 1
            print(left, right)

        return target == nums[left]

191 - Number of 1 Bits

leetcode

Problem

Write a function that takes an unsigned integer and return the number of '1' bits it has (also known as the Hamming weight).

Example 1:

Input: 00000000000000000000000000001011
Output: 3
Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits.

Example 2:

Input: 00000000000000000000000010000000
Output: 1
Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit.

Example 3:

Input: 11111111111111111111111111111101
Output: 31
Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits.

Solution

class Solution:
    def hammingWeight(self, n: int) -> int:
        ans = 0
        while n:
            ans += n % 2
            n //= 2

        return ans

82 - Remove Duplicates from Sorted List II

leetcode

Problem

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

Return the linked list sorted as well.

Example 1:

Input: 1->2->3->3->4->4->5
Output: 1->2->5
Example 2:

Input: 1->1->1->2->3
Output: 2->3

Solution

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        extra = ListNode(None)

        extra.next = head
        pre = extra

        while head and head.next:
            if head.val == head.next.val:
                while head and head.next and head.val == head.next.val:
                    head = head.next

                head = head.next
                pre.next = head
            else:
                pre = head
                head = head.next

        return extra.next

240 - Search a 2D Matrix II

leetcode

Problem

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

Integers in each row are sorted in ascending from left to right.
Integers in each column are sorted in ascending from top to bottom.

Example:

Consider the following matrix:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
Given target = 5, return true.

Given target = 20, return false.

Solution

Solution 1

class Solution:
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if not matrix or not matrix[0]:
            return False

        j = 0

        for row in matrix:
            if row[j] > target:
                while row[j] > target:
                    j -= 1
                    if j < 0:
                        return False


            elif row[j] < target:
                while row[j] < target:
                    j += 1
                    if j == len(matrix[0]):
                        j = 0
                        break


            if row[j] == target:
                return True

        return False

Solution 2: Like a binary tree

Start from upper right corner.

If matrix[i][j] > target: col - 1

if matrix[i][j] < target: row + 1

class Solution:
    def searchMatrix(self, matrix, target):
        if not matrix or not matrix[0]:
            return False

        i, j = 0, len(matrix[0]) - 1
        while i < len(matrix) and j >= 0 :

            if matrix[i][j] == target:
                return True
            elif matrix[i][j] > target:
                j -= 1
            else:
                i += 1
        return False

1103 - Distribute Candies to People

leetcode

Problem

We distribute some number of candies, to a row of n = num_people people in the following way:

We then give 1 candy to the first person, 2 candies to the second person, and so on until we give n candies to the last person.

Then, we go back to the start of the row, giving n + 1 candies to the first person, n + 2 candies to the second person, and so on until we give 2 * n candies to the last person.

This process repeats (with us giving one more candy each time, and moving to the start of the row after we reach the end) until we run out of candies.  The last person will receive all of our remaining candies (not necessarily one more than the previous gift).

Return an array (of length num_people and sum candies) that represents the final distribution of candies.

Example 1:

Input: candies = 7, num_people = 4
Output: [1,2,3,1]
Explanation:
On the first turn, ans[0] += 1, and the array is [1,0,0,0].
On the second turn, ans[1] += 2, and the array is [1,2,0,0].
On the third turn, ans[2] += 3, and the array is [1,2,3,0].
On the fourth turn, ans[3] += 1 (because there is only one candy left), and the final array is [1,2,3,1].

Example 2:

Input: candies = 10, num_people = 3
Output: [5,2,3]
Explanation:
On the first turn, ans[0] += 1, and the array is [1,0,0].
On the second turn, ans[1] += 2, and the array is [1,2,0].
On the third turn, ans[2] += 3, and the array is [1,2,3].
On the fourth turn, ans[0] += 4, and the final array is [5,2,3].

Solution

class Solution:
    def distributeCandies(self, candies: int, num_people: int) -> List[int]:
        i = 1
        ans = [0] * num_people
        while candies >= 0:
            ans[(i-1) % num_people] += min(candies, i)
            candies -= i
            i += 1
        return ans

148 - Sort List

leetcode

Problem

Sort a linked list in O(n log n) time using constant space complexity.

Example 1:

Input: 4->2->1->3
Output: 1->2->3->4
Example 2:

Input: -1->5->3->4->0
Output: -1->0->3->4->5

Solution

Merge Sort

  1. Slow-Fast-Pointer to split the list into half. Do not forget slow.next = None
  2. Sort two part recursively and merge together
class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head

        # split in half
        fast = slow = head
        while fast.next and fast.next.next:
            fast = fast.next.next
            slow = slow.next

        second = slow.next
        slow.next = None

        first = self.sortList(head)
        second = self.sortList(second)

        # merge
        return self.merge(first, second)

    def merge(self, p1, p2):
        p = dummy = ListNode(0)

        while p1 and p2:
            if p1.val < p2.val:
                p.next, p1 = p1, p1.next
            else:
                p.next, p2 = p2, p2.next
            p = p.next

        p.next = p1 or p2

        return dummy.next

142 - Reorder List

leetcode

Problem

Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

You may not modify the values in the list's nodes, only nodes itself may be changed.

Example 1:

Given 1->2->3->4, reorder it to 1->4->2->3.
Example 2:

Given 1->2->3->4->5, reorder it to 1->5->2->4->3.

Solution

class Solution:
    def reorderList(self, head: ListNode) -> None:
        if not head or not head.next:
            return
        fast = slow = head

        while fast.next and fast.next.next:
            fast = fast.next.next
            slow = slow.next

        p1, p2 = slow, slow.next
        slow.next = None

        while p2:
            p2.next, p2, p1 = p1, p2.next, p2

        first = head
        tail = p1

        while tail and first:

            first.next, tail.next, first, tail = tail, first.next, first.next, tail.next

967 - Numbers With Same Consecutive Differences

leetcode

Problem

Return all non-negative integers of length N such that the absolute difference between every two consecutive digits is K.

Note that every number in the answer must not have leading zeros except for the number 0 itself. For example, 01 has one leading zero and is invalid, but 0 is valid.

You may return the answer in any order.

Example 1:

Input: N = 3, K = 7
Output: [181,292,707,818,929]
Explanation: Note that 070 is not a valid number, because it has leading zeroes.
Example 2:

Input: N = 2, K = 1
Output: [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98]

Solution

It is a simple queue problem.

Two corner cases to notice:

  1. remember to include 0 in the answers if the N == 1
  2. if K == 0, do not add +K, -K twice. They have the same value.
class Solution:
    def numsSameConsecDiff(self, N: int, K: int) -> List[int]:
        ans = [i for i in range(1, 10)]

        if N == 1:
            return [0] + ans

        digits = 10 ** (N-1)

        while ans[0] / digits < 1:
            cur = ans.pop(0)
            last_digit = cur % 10
            if K == 0:
                ans.append(cur * 10 + last_digit)
            else:
                if last_digit + K < 10:
                    ans.append(cur * 10 + last_digit + K)
                if last_digit - K >= 0:
                    ans.append(cur * 10 + last_digit - K)
        return ans

97 - Interleaving String

leetcode

Problem

Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.

Example 1:

Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
Output: true
Example 2:

Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
Output: false

Solution

DP problem.

Imagin you have a matrix with s1 as its row and s2 as its column.

You have to find a path from upper left corner to bottom right corner, which consisits the s3.

So when you walk the path, you have two options:

  1. you come from left. dp[i][j] = dp[i][j-1] and s1[j-1] == s3[i+j-1]
  2. you come from above. dp[i][j] = dp[i][j-1] and s2[i-1] == s3[i+j-1]
class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        if len(s1) + len(s2) != len(s3):
            return False
        dp = [[False] * (len(s1)+1) for _ in range(len(s2)+1)]
        for i in range(len(s2)+1):
            for j in range(len(s1)+1):
                if not i and not j:
                    dp[i][j] = True
                elif not i:
                    dp[i][j] = dp[0][j-1] and s1[j-1] == s3[j-1]
                elif not j:
                    dp[i][j] = dp[i-1][0] and s2[i-1] == s3[i-1]
                else:
                    dp[i][j] = (dp[i-1][j] and s2[i-1] == s3[i+j-1]) or (dp[i][j-1] and s1[j-1] == s3[i+j-1])

        return dp[-1][-1]

108 - Convert Sorted Array to Binary Search Tree

leetcode

Problem

Given an array where elements are sorted in ascending order, convert it to a height balanced BST.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

Example:

Given the sorted array: [-10,-3,0,5,9],

One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST:

      0
     / \
   -3   9
   /   /
 -10  5

Solution

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums:
            return None

        mid = len(nums)//2

        root = TreeNode(nums[mid])
        root.left = self.sortedArrayToBST(nums[:mid])
        root.right = self.sortedArrayToBST(nums[mid+1:])

        return root

109 - Convert Sorted List to Binary Search Tree

leetcode

Problem

Given the head of a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.

For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

Solution

class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        if not head:
            return None
        if not head.next:
            return TreeNode(head.val)

        pre, slow, fast = None, head, head

        while fast and fast.next:
            pre, slow, fast = slow, slow.next, fast.next.next

        pre.next = None
        root = TreeNode(slow.val)
        root.left = self.sortedListToBST(head)
        root.right = self.sortedListToBST(slow.next)

        return root

110 - Balanced Binary Tree

leetcode

Problem

Given a binary tree, determine if it is height-balanced.

For this problem, a height-balanced binary tree is defined as:

a binary tree in which the left and right subtrees of every node differ in height by no more than 1.

Example 1:

Given the following tree [3,9,20,null,null,15,7]:

    3
   / \
  9  20
    /  \
   15   7
Return true.

Example 2:

Given the following tree [1,2,2,3,3,null,null,4,4]:

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4
Return false.

Solution

Solution 1: multiple pass

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root:
            return True

        hr = self.height(root.right)
        hl = self.height(root.left)

        return self.isBalanced(root.right) and self.isBalanced(
            root.left) and abs(hr - hl) <= 1

    def height(self, root):
        if not root:
            return 0
        return 1 + max(self.height(root.right), self.height(root.left))

This method has to calculate the height mutiple times, which costs a lot of time.

Solution 2: one pass

It is a dfs method, which only go over all nodes one time.

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        return self.height(root) != -1

    def height(self, root):
        if not root:
            return 0
        lh = self.height(root.right)
        if lh == -1:
            return -1
        rh = self.height(root.left)
        if rh == -1:
            return -1
        if abs(lh - rh) > 1:
            return -1
        return max(lh, rh) + 1

111 - Minimum Depth of Binary Tree

leetcode

Problem

Given a binary tree, find its minimum depth.

The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.

Note: A leaf is a node with no children.

Example:

Given binary tree [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
return its minimum depth = 2.

Solution

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root:
            return 0

        if not root.right:
            return self.minDepth(root.left) + 1
        elif not root.left:
            return self.minDepth(root.right) + 1
        else:
            return min(self.minDepth(root.right), self.minDepth(root.left)) + 1
class Solution:
    def minDepth(self, root):
        if not root: return 0
        dl = self.minDepth(root.left)
        dr = self.minDepth(root.right)
        return 1 + (min(dl, dr) or max(dl, dr))

824 - Goat Latin

leetcode

Problem

A sentence S is given, composed of words separated by spaces. Each word consists of lowercase and uppercase letters only.

We would like to convert the sentence to "Goat Latin" (a made-up language similar to Pig Latin.)

The rules of Goat Latin are as follows:

If a word begins with a vowel (a, e, i, o, or u), append "ma" to the end of the word.
For example, the word 'apple' becomes 'applema'.

If a word begins with a consonant (i.e. not a vowel), remove the first letter and append it to the end, then add "ma".
For example, the word "goat" becomes "oatgma".

Add one letter 'a' to the end of each word per its word index in the sentence, starting with 1.
For example, the first word gets "a" added to the end, the second word gets "aa" added to the end and so on.
Return the final sentence representing the conversion from S to Goat Latin.



Example 1:

Input: "I speak Goat Latin"
Output: "Imaa peaksmaaa oatGmaaaa atinLmaaaaa"
Example 2:

Input: "The quick brown fox jumped over the lazy dog"
Output: "heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa"


Notes:

S contains only uppercase, lowercase and spaces. Exactly one space between each word.
1 <= S.length <= 150.

Solution

Interesting one-liner

class Solution:
    def toGoatLatin(self, S: str) -> str:
        return ' '.join([(lambda w: w[1:]+w[0] if w[0] not in 'aeiouAEIOU' else w)(w) + 'ma' + 'a' * i for i, w in enumerate(S.split(' '), 1)])

114 - Flatten Binary Tree to Linked List

leetcode

Problem

Given a binary tree, flatten it to a linked list in-place.

For example, given the following tree:

    1
   / \
  2   5
 / \   \
3   4   6
The flattened tree should look like:

1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

Solution

  1. Traverse the tree in reverse preorder, the opposite of root-left-right.
  2. Save the root, and use it in the upper level.
class Solution:
    def flatten(self, root: TreeNode) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        self.pre = None
        self.dfs(root)

    def dfs(self, root):

        if not root:
            return

        self.dfs(root.right)
        self.dfs(root.left)

        root.right = self.pre
        self.pre = root
        root.left = None

368 - Largest Divisible Subset

leetcode

Problem

Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of elements in this subset satisfies:

Si % Sj = 0 or Sj % Si = 0.

If there are multiple solutions, return any subset is fine.

Example 1:

Input: [1,2,3]
Output: [1,2] (of course, [1,3] will also be ok)

Example 2:

Input: [1,2,4,8]
Output: [1,2,4,8]

Solution

Solution 1: DP

class Solution:
    def largestDivisibleSubset(self, nums: List[int]) -> List[int]:
        if not nums:
            return nums
        dp = [1] * len(nums)
        pre = [i for i in range(len(nums))]
        nums.sort()

        max_i = 0

        for i in range(len(dp)):
            for j in range(i):
                if nums[i] % nums[j] == 0 and dp[j] + 1 > dp[i]:
                    dp[i] = dp[j] + 1
                    pre[i] = j

                if dp[i] > dp[max_i]:
                    max_i = i

        ans = []
        i = max_i
        while pre[i] != i:
            ans.append(nums[i])
            i = pre[i]
        ans.append(nums[i])

        return ans

Solution 2: Hash

  1. Sort the numbers
  2. Store the answers for every number while go through the numbers
class Solution:
    def largestDivisibleSubset(self, nums):
        S = {-1: set()}
        H = {}
        for n in sorted(nums):
            H[n] = max((H[d] for d in S if x % d == 0), key=len) | {n}
        return list(max(S.values(), key=len))

784 - Letter Case Permutation

leetcode

Problem

Given a string S, we can transform every letter individually to be lowercase or uppercase to create another string.

Return a list of all possible strings we could create. You can return the output  in any order.



Example 1:

Input: S = "a1b2"
Output: ["a1b2","a1B2","A1b2","A1B2"]
Example 2:

Input: S = "3z4"
Output: ["3z4","3Z4"]
Example 3:

Input: S = "12345"
Output: ["12345"]
Example 4:

Input: S = "0"
Output: ["0"]


Constraints:

S will be a string with length between 1 and 12.
S will consist only of letters or digits.

Solution

class Solution:
    def letterCasePermutation(self, S: str) -> List[str]:
        ans = []
        self.dfs(S, 0, ans, [])
        return ans

    def dfs(self, S, i, ans, path):
        if i == len(S):
            ans.append(''.join(path))
            return

        self.dfs(S, i+1, ans, path + [S[i]])

        if S[i].isalpha():
            self.dfs(S, i+1, ans, path + [S[i].swapcase()])

1079 - Letter Tile Possibilities

leetcode

Problem

You have n  tiles, where each tile has one letter tiles[i] printed on it.

Return the number of possible non-empty sequences of letters you can make using the letters printed on those tiles.



Example 1:

Input: tiles = "AAB"
Output: 8
Explanation: The possible sequences are "A", "B", "AA", "AB", "BA", "AAB", "ABA", "BAA".
Example 2:

Input: tiles = "AAABBC"
Output: 188
Example 3:

Input: tiles = "V"
Output: 1


Constraints:

1 <= tiles.length <= 7
tiles consists of uppercase English letters.

Solution

class Solution:
    def numTilePossibilities(self, tiles: str) -> int:
        self.ans = 0
        mark = [False] * len(tiles)
        self.dfs(sorted(tiles), mark)
        return self.ans

    def dfs(self, tiles, mark):
        for i in range(len(tiles)):
            if mark[i] or (i > 0 and tiles[i] == tiles[i-1] and not mark[i-1]):
                continue
            mark[i] = True
            self.ans += 1
            self.dfs(tiles, mark)
            mark[i] = False

516 - Longest Palindromic Subsequence

leetcode

Problem

Given a string s, find the longest palindromic subsequence's length in s. You may assume that the maximum length of s is 1000.

Example 1:
Input:

"bbbab"
Output:
4
One possible longest palindromic subsequence is "bbbb".


Example 2:
Input:

"cbbd"
Output:
2
One possible longest palindromic subsequence is "bb".


Constraints:

1 <= s.length <= 1000
s consists only of lowercase English letters.

Solution

DP problem.

dp[i][j] represents the max value for substring from j to i.

Transition:

if s[i] == s[j]: dp[i][j] = dp[i-1][j+1] + 2 else: dp[i][j] = max(dp[i-1][j] + dp[i][j+1])

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        dp = [[0] * len(s) for i in range(len(s))]

        for i in range(0, len(s)):
            dp[i][i] = 1
            for j in range(i-1, -1, -1):
                if s[i] == s[j]:
                    dp[i][j] = dp[i-1][j+1] + 2
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j+1])
        return dp[-1][0] if s else 0

1032 - Stream of Characters

leetcode

Problem

Implement the StreamChecker class as follows:

StreamChecker(words): Constructor, init the data structure with the given words.
query(letter): returns true if and only if for some k >= 1, the last k characters queried (in order from oldest to newest, including this letter just queried) spell one of the words in the given list.


Example:

StreamChecker streamChecker = new StreamChecker(["cd","f","kl"]); // init the dictionary.
streamChecker.query('a');          // return false
streamChecker.query('b');          // return false
streamChecker.query('c');          // return false
streamChecker.query('d');          // return true, because 'cd' is in the wordlist
streamChecker.query('e');          // return false
streamChecker.query('f');          // return true, because 'f' is in the wordlist
streamChecker.query('g');          // return false
streamChecker.query('h');          // return false
streamChecker.query('i');          // return false
streamChecker.query('j');          // return false
streamChecker.query('k');          // return false
streamChecker.query('l');          // return true, because 'kl' is in the wordlist


Note:

1 <= words.length <= 2000
1 <= words[i].length <= 2000
Words will only consist of lowercase English letters.
Queries will only consist of lowercase English letters.
The number of queries is at most 40000.

Solution

It’s clearly a trie problem. But how to optimize it?

We must keep trace of the stream, and try to search the every pattern in the trace. But that would be LTE.

In order not to loop through every starting point of the trace, we do it reversely, so we only need to start from the beginning once and stop for the existing word.

For example:

[“dlab”, “xlab”]

Create a reversed trie [“bald”, “balx”]

trace = “”, max length of trace = 4, because, all words in the trie are not longer than 4.

“b”: trace = “b”, search for “b” in the trie, False “a”: trace = “ba”, search for “ba” in the trie, False “l”: trace = “bal”, search for “bal” in the trie, False “d”: trace = “bald”, search for “bald” in the trie, True “x”: trace = “aldx”, search for “aldx” in the trie, False

Steps:

  1. create the trie with reversed words
  2. keep the track of the stream with a max possible length
  3. search the track of the stream in the “reversed” trie.
class StreamChecker:

    def __init__(self, words: List[str]):
        self.root = TrieNode()

        for w in words:
            x = self.root
            for c in reversed(w):
                if c in x.children:
                    x = x.children[c]
                else:
                    x.children[c] = TrieNode()
                    x = x.children[c]
            x.is_word = True

        self.s = ""
        self.w = max(map(len, words))

    def query(self, letter: str) -> bool:

        self.s = (letter+self.s)[:self.w]
        x = self.root
        res = False

        for c in self.s:
            if c in x.children:
                x = x.children[c]
                if x.is_word:
                    return True
            else:
                return False

        return res


class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_word = False

Simplified Version

import collections
from functools import reduce

class StreamChecker:
    def __init__(self, words):
        T = lambda: collections.defaultdict(T)
        self.trie = T()
        for w in words: reduce(dict.__getitem__, w[::-1], self.trie)["#"] = True
        self.s = ""
        self.w = max(map(len, words))

    def query(self, letter):
        self.s = (letter + self.s)[:self.w]
        cur = self.trie
        for c in self.s:
            if c in cur:
                cur = cur[c]
                if c["#"] == True:
                    return True
            else:
                break

        return False

199 - Binary Tree Right Side View

leetcode

Problem

Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.

Example:

Input: [1,2,3,null,5,null,4]
Output: [1, 3, 4]
Explanation:

   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---

Solution

Comprare the depth. DFS.

Keep track of the current depth. And the length of current answer is the previous depth.

Traverse right to left

If current_depth > previous depth, then it appears in the right view.

class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        ans = []
        self.print(root, ans, 0)
        return ans

    def print(self, root, ans, depth):
        if not root:
            return

        if depth + 1 > len(ans): ans.append(root.val)

        self.print(root.right, ans, depth+1)
        self.print(root.left, ans, depth+1)

1219 - Path with Maximum Gold

leetcode

Problem

In a gold mine grid of size m * n, each cell in this mine has an integer representing the amount of gold in that cell, 0 if it is empty.

Return the maximum amount of gold you can collect under the conditions:

Every time you are located in a cell you will collect all the gold in that cell.
From your position you can walk one step to the left, right, up or down.
You can't visit the same cell more than once.
Never visit a cell with 0 gold.
You can start and stop collecting gold from any position in the grid that has some gold.


Example 1:

Input: grid = [[0,6,0],[5,8,7],[0,9,0]]
Output: 24
Explanation:
[[0,6,0],
 [5,8,7],
 [0,9,0]]
Path to get the maximum gold, 9 -> 8 -> 7.
Example 2:

Input: grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
Output: 28
Explanation:
[[1,0,7],
 [2,0,6],
 [3,4,5],
 [0,3,0],
 [9,0,20]]
Path to get the maximum gold, 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7.

Solution

class Solution:
    def getMaximumGold(self, grid: List[List[int]]) -> int:
        self.ans = 0
        mark = [[False] * len(grid[0]) for _ in grid]

        for i in range(len(grid)):
            for j in range(len(grid[0])):
                self.dfs(grid, i, j, mark, 0)

        return self.ans

    def dfs(self, grid, i, j, mark, gold):

        if i < 0 or i == len(grid) or j < 0 or j == len(grid[0]) or mark[i][j] or not grid[i][j]:
            self.ans = max(self.ans, gold)
            return

        mark[i][j] = True

        for di, dj in [[1,0], [0, 1], [-1, 0], [0, -1]]:
            self.dfs(grid, i+di, j+dj, mark, gold+grid[i][j])

        mark[i][j] = False

404 - Sum of Left Leaves

leetcode

Problem

Find the sum of all left leaves in a given binary tree.

Example:

    3
   / \
  9  20
    /  \
   15   7

There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.

Solution

class Solution:
    def sumOfLeftLeaves(self, root: TreeNode) -> int:
        return self.dfs(root, 0, False)

    def dfs(self, root, presum, isLeft):
        if not root:
            return 0
        if not root.left and not root.right and isLeft:
            return presum + root.val
        else:
            return self.dfs(root.left, presum, True) + self.dfs(root.right, presum, False)

131 - Palindrome Partitioning

leetcode

Problem

Given a string s, partition s such that every substring of the partition is a palindrome.

Return all possible palindrome partitioning of s.

Example:

Input: "aab"
Output:
[
  ["aa","b"],
  ["a","a","b"]
]

Solution

class Solution:
    def partition(self, s: str) -> List[List[str]]:
        ans = []
        self.dfs(s, 0, ans, [])
        return ans

    def dfs(self, s, start, ans, path):

        if start == len(s):
            ans.append(path.copy())
            return

        for i in range(start, len(s)):
            w = s[start:i + 1]
            if w == w[::-1]:
                path.append(w)
                self.dfs(s, i + 1, ans, path)
                path.pop()

132 - Palindrome Partitioning II

leetcode

Problem

Given a string s, partition s such that every substring of the partition is a palindrome

Return the minimum cuts needed for a palindrome partitioning of s.

Example 1:

Input: s = "aab"
Output: 1
Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.
Example 2:

Input: s = "a"
Output: 0
Example 3:

Input: s = "ab"
Output: 1


Constraints:

1 <= s.length <= 2000
s consists of lower-case English letters only.

Solution

If s[i:j+1] is a palindrome, and if we know s[0:i] has a minCut of X, then we know minCut to for s[0:j+1] is not greater than X+1.

class Solution:
    def minCut(self, s: str) -> int:
        cut = [x for x in range(-1, len(s))]
        for i in range(len(s)):
            for j in range(i, len(s)):
                if s[i:j+1] == s[i:j+1][::-1]:
                    cut[j+1] = min(cut[j+1], cut[i]+1)

        return cut[-1]

983 - Minimum Cost For Tickets

leetcode

Problem

In a country popular for train travel, you have planned some train travelling one year in advance.  The days of the year that you will travel is given as an array days.  Each day is an integer from 1 to 365.

Train tickets are sold in 3 different ways:

a 1-day pass is sold for costs[0] dollars;
a 7-day pass is sold for costs[1] dollars;
a 30-day pass is sold for costs[2] dollars.
The passes allow that many days of consecutive travel.  For example, if we get a 7-day pass on day 2, then we can travel for 7 days: day 2, 3, 4, 5, 6, 7, and 8.

Return the minimum number of dollars you need to travel every day in the given list of days.



Example 1:

Input: days = [1,4,6,7,8,20], costs = [2,7,15]
Output: 11
Explanation:
For example, here is one way to buy passes that lets you travel your travel plan:
On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1.
On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9.
On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20.
In total you spent $11 and covered all the days of your travel.
Example 2:

Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15]
Output: 17
Explanation:
For example, here is one way to buy passes that lets you travel your travel plan:
On day 1, you bought a 30-day pass for costs[2] = $15 which covered days 1, 2, ..., 30.
On day 31, you bought a 1-day pass for costs[0] = $2 which covered day 31.
In total you spent $17 and covered all the days of your travel.

Solution

DP problem. dp[i] represents the min cost for ith day.

We have four options for one day.

  1. We don’t travel: dp[i] = dp[i-1]

  2. We have to travel:

    a. buy 1-day ticket: dp[i] = dp[i-1] + costs[0] b. buy 7-days ticket which can cover ith day: dp[i] = min(dp[i-1], dp[i-2], dp[i-3], … dp[i-7]) + costs[1] c. buy 30-days ticket which can cover ith day: dp[i] = min(dp[i-1], dp[i-2], … dp[i-30]) + costs[2]

Important is to know, the min cost is always increasing.

So min(dp[i-1], dp[i-2]… dp[i-7]) = dp[i-7]

dp[i] = min(dp[i-1] + costs[0], dp[i-7] + costs[0], dp[i-30] + costs[0])

Note that, i-7 and i-30 might smaller than 0 in the loop. Use max(0, i-7) to avoid that.

class Solution:
    def mincostTickets(self, days: List[int], costs: List[int]) -> int:
        dp = [0] * (max(days)+1)

        for i in range(1, len(dp)):
            if i not in days:
                dp[i] = dp[i-1]
            else:
                dp[i] = min(dp[i-1]+costs[0], dp[max(0, i-7)] + costs[1], dp[max(0, i-30)] + costs[2])

        return dp[-1]

412 - Fizz Buzz

leetcode

Problem

Write a program that outputs the string representation of numbers from 1 to n.

But for multiples of three it should output “Fizz” instead of the number and for the multiples of five output “Buzz”. For numbers which are multiples of both three and five output “FizzBuzz”.

Example:

n = 15,

Return:
[
    "1",
    "2",
    "Fizz",
    "4",
    "Buzz",
    "Fizz",
    "7",
    "8",
    "Fizz",
    "Buzz",
    "11",
    "Fizz",
    "13",
    "14",
    "FizzBuzz"
]

Solution

class Solution:
    def fizzBuzz(self, n: int) -> List[str]:
        ans = []

        for i in range(1, n+1):

            if not i % 3 and not i % 5:
                ans.append("FizzBuzz")
            elif not i % 5:
                ans.append("Buzz")
            elif not i % 3:
                ans.append("Fizz")
            else:
                ans.append(str(i))

        return ans

1286 - Iterator for Combination

leetcode

Problem

Design an Iterator class, which has:

A constructor that takes a string characters of sorted distinct lowercase English letters and a number combinationLength as arguments.
A function next() that returns the next combination of length combinationLength in lexicographical order.
A function hasNext() that returns True if and only if there exists a next combination.


Example:

CombinationIterator iterator = new CombinationIterator("abc", 2); // creates the iterator.

iterator.next(); // returns "ab"
iterator.hasNext(); // returns true
iterator.next(); // returns "ac"
iterator.hasNext(); // returns true
iterator.next(); // returns "bc"
iterator.hasNext(); // returns false


Constraints:

1 <= combinationLength <= characters.length <= 15
There will be at most 10^4 function calls per test.
It's guaranteed that all calls of the function next are valid.

Solution

Solution 1: DFS to generate the bit mask

class CombinationIterator:

    def __init__(self, characters: str, combinationLength: int):
        self.bitmasks = []
        self.characters = characters
        self.combinationLength = combinationLength
        self.dfs(0, 0, [])
        print(self.bitmasks)

    def next(self) -> str:
        ans = ""
        mask = self.bitmasks.pop(0)
        for i in range(len(mask)):
            if mask[i] == "1":
                ans += self.characters[i]
        return ans

    def hasNext(self) -> bool:
        return self.bitmasks != []

    def dfs(self, count, index, path):

        if index == len(self.characters):
            if count == self.combinationLength:
                self.bitmasks.append(list(path))
            return

        for i in ["1", "0"]:
            if count == self.combinationLength and i == "1":
                continue

            path.append(i)
            if i == "1":
                self.dfs(count+1, index+1, path)
            else:
                self.dfs(count, index+1, path)

            path.pop()

Solution 2: Generate the bismask iteratively

class CombinationIterator:
    def __init__(self, characters: str, combinationLength: int):
        self.characters = characters
        self.combinationLength = combinationLength
        self.bitmasks = []
        for mask in range(1 << len(characters)):
            if bin(mask).count('1') == self.combinationLength:
                res = ""
                for i in range(len(characters)):
                    res = str(mask%2) + res
                    mask >>= 1
                self.bitmasks.append(res)
        self.bitmasks = self.bitmasks[::-1]


    def next(self) -> str:
        ans = ""
        mask = self.bitmasks.pop(0)
        for i in range(len(mask)):
            if mask[i] == "1":
                ans += self.characters[i]
        return ans

    def hasNext(self) -> bool:
        return self.bitmasks != []

93 - Restore IP Addresses

leetcode

Problem

Given a string s containing only digits. Return all possible valid IP addresses that can be obtained from s. You can return them in any order.

A valid IP address consists of exactly four integers, each integer is between 0 and 255, separated by single points and cannot have leading zeros. For example, "0.1.2.201" and "192.168.1.1" are valid IP addresses and "0.011.255.245", "192.168.1.312" and "192.168@1.1" are invalid IP addresses.



Example 1:

Input: s = "25525511135"
Output: ["255.255.11.135","255.255.111.35"]
Example 2:

Input: s = "0000"
Output: ["0.0.0.0"]
Example 3:

Input: s = "1111"
Output: ["1.1.1.1"]
Example 4:

Input: s = "010010"
Output: ["0.10.0.10","0.100.1.0"]
Example 5:

Input: s = "101023"
Output: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]


Constraints:

0 <= s.length <= 3000
s consists of digits only.

Solution

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        if len(s) > 12:
            return []

        ans = []
        self.dfs(s, 0, ans, [])
        return ans

    def dfs(self, s, start, ans, path):
        if start >= len(s):
            if len(path) == 4:
                ans.append(".".join(path))
            return

        for i in range(start, min(len(s), start+3)):
            number = s[start:i+1]
            if len(number) > 1 and number[0] == "0":
                continue

            if int(number) <= 255:
                path.append(number)
                self.dfs(s, i+1, ans, path)
                path.pop()

436 - Find Right Interval

leetcode

Problem

Given a set of intervals, for each of the interval i, check if there exists an interval j whose start point is bigger than or equal to the end point of the interval i, which can be called that j is on the "right" of i.

For any interval i, you need to store the minimum interval j's index, which means that the interval j has the minimum start point to build the "right" relationship for interval i. If the interval j doesn't exist, store -1 for the interval i. Finally, you need output the stored value of each interval as an array.

Note:

You may assume the interval's end point is always bigger than its start point.
You may assume none of these intervals have the same start point.


Example 1:

Input: [ [1,2] ]

Output: [-1]

Explanation: There is only one interval in the collection, so it outputs -1.


Example 2:

Input: [ [3,4], [2,3], [1,2] ]

Output: [-1, 0, 1]

Explanation: There is no satisfied "right" interval for [3,4].
For [2,3], the interval [3,4] has minimum-"right" start point;
For [1,2], the interval [2,3] has minimum-"right" start point.


Example 3:

Input: [ [1,4], [2,3], [3,4] ]

Output: [-1, 2, -1]

Explanation: There is no satisfied "right" interval for [1,4] and [3,4].
For [2,3], the interval [3,4] has minimum-"right" start point.

Solution

class Solution:
    def findRightInterval(self, intervals: List[List[int]]) -> List[int]:
        starts = sorted([[a[0], i] for i, a in enumerate(intervals)]) + [[float('inf'), -1]]
        return [starts[bisect.bisect(starts, [x[1]])][1] for x in intervals]

980 - Unique Paths III

leetcode

Problem

On a 2-dimensional grid, there are 4 types of squares:

1 represents the starting square.  There is exactly one starting square.
2 represents the ending square.  There is exactly one ending square.
0 represents empty squares we can walk over.
-1 represents obstacles that we cannot walk over.
Return the number of 4-directional walks from the starting square to the ending square, that walk over every non-obstacle square exactly once.



Example 1:

Input: [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
Output: 2
Explanation: We have the following two paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
Example 2:

Input: [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
Output: 4
Explanation: We have the following four paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
Example 3:

Input: [[0,1],[2,0]]
Output: 0
Explanation:
There is no path that walks over every empty square exactly once.
Note that the starting and ending square can be anywhere in the grid.


Note:

1 <= grid.length * grid[0].length <= 20

Solution

class Solution:
    def uniquePathsIII(self, grid: List[List[int]]) -> int:
        count = len([x for r in grid for x in r if x == 0]) + 2
        start = [[i,j] for i in range(len(grid)) for j in range(len(grid[0])) if grid[i][j] == 1][0]
        self.ans = 0
        self.dfs(grid, start[0], start[1], 0, count)
        return self.ans

    def dfs(self, grid, i, j, curCount, count):

        if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] in [-1, 100]:
            return

        if curCount == count-1 and grid[i][j] == 2:
            self.ans += 1
            return
        elif grid[i][j] == 2:
            return

        x = grid[i][j]
        grid[i][j] = 100
        for di, dj in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
            self.dfs(grid, i+di, j+dj, curCount+1, count)

        grid[i][j] = x

450 - Delete Node in a BST

leetcode

Problem

Design an Iterator class, which has:

A constructor that takes a string characters of sorted distinct lowercase English letters and a number combinationLength as arguments.
A function next() that returns the next combination of length combinationLength in lexicographical order.
A function hasNext() that returns True if and only if there exists a next combination.


Example:

CombinationIterator iterator = new CombinationIterator("abc", 2); // creates the iterator.

iterator.next(); // returns "ab"
iterator.hasNext(); // returns true
iterator.next(); // returns "ac"
iterator.hasNext(); // returns true
iterator.next(); // returns "bc"
iterator.hasNext(); // returns false


Constraints:

1 <= combinationLength <= characters.length <= 15
There will be at most 10^4 function calls per test.
It's guaranteed that all calls of the function next are valid.

Solution

class Solution:
    def deleteNode(self, root: TreeNode, key: int) -> TreeNode:

        if not root:
            return None

        if root.val > key:
            root.left = self.deleteNode(root.left, key)
        elif root.val < key:
            root.right = self.deleteNode(root.right, key)
        else:
            if not root.left:
                root = root.right
            elif not root.right:
                root = root.left
            else:
                node = self.getMin(root.right)
                node.left = root.left
                root = root.right

        return root

    def getMin(self, root):
        if not root:
            return None

        return self.getMin(root.left) or root

526 - Beautiful Arrangement

leetcode

Problem

Suppose you have N integers from 1 to N. We define a beautiful arrangement as an array that is constructed by these N numbers successfully if one of the following is true for the ith position (1 <= i <= N) in this array:

The number at the ith position is divisible by i.
i is divisible by the number at the ith position.


Now given N, how many beautiful arrangements can you construct?

Example 1:

Input: 2
Output: 2
Explanation:

The first beautiful arrangement is [1, 2]:

Number at the 1st position (i=1) is 1, and 1 is divisible by i (i=1).

Number at the 2nd position (i=2) is 2, and 2 is divisible by i (i=2).

The second beautiful arrangement is [2, 1]:

Number at the 1st position (i=1) is 2, and 2 is divisible by i (i=1).

Number at the 2nd position (i=2) is 1, and i (i=2) is divisible by 1.


Note:

N is a positive integer and will not exceed 15.

Solution

class Solution:
    def countArrangement(self, N: int) -> int:
        self.ans = 0
        self.mark = [False for i in range(N+1)]
        self.dfs(N, self.mark, [])
        return self.ans

    def dfs(self, N, mark, path):
        if len(path) == N:
            self.ans += 1

        for n in range(1, N+1):
            index = len(path) + 1
            if mark[n] or (n % index != 0 and index % n != 0):
                continue
            mark[n] = True
            self.dfs(N, self.mark, path + [n])
            mark[n] = False

354 - Russian Doll Envelopes

leetcode

Problem

You have a number of envelopes with widths and heights given as a pair of integers (w, h). One envelope can fit into another if and only if both the width and height of one envelope is greater than the width and height of the other envelope.

What is the maximum number of envelopes can you Russian doll? (put one inside other)

Note:
Rotation is not allowed.

Example:

Input: [[5,4],[6,4],[6,7],[2,3]]
Output: 3
Explanation: The maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]).

Solution

class Solution:
    def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
        if not envelopes:
            return 0

        heights = [env[1] for env in sorted(envelopes, key = lambda x: [x[0], -x[1]])]
        print(heights)
        dp = [heights[0]]

        for i in range(1, len(heights)):
            if heights[i] > dp[-1]:
                dp.append(heights[i])
            else:
                l = self.bs(dp, heights[i])

                dp[l] = heights[i]
        return len(dp)

    def bs(self, dp, target):
        r = len(dp) - 1
        l = 0
        while l <= r:
            mid = (l+r) // 2
            if dp[mid] > target:
                r = mid - 1
            elif dp[mid] < target:
                l = mid + 1

            else:
                return mid
        return l

403 - Frog Jump

leetcode

Problem

A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water.

Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit.

If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction.

Note:

The number of stones is ≥ 2 and is < 1,100.
Each stone's position will be a non-negative integer < 231.
The first stone's position is always 0.
Example 1:

[0,1,3,5,6,8,12,17]

There are a total of 8 stones.
The first stone at the 0th unit, second stone at the 1st unit,
third stone at the 3rd unit, and so on...
The last stone at the 17th unit.

Return true. The frog can jump to the last stone by jumping
1 unit to the 2nd stone, then 2 units to the 3rd stone, then
2 units to the 4th stone, then 3 units to the 6th stone,
4 units to the 7th stone, and 5 units to the 8th stone.
Example 2:

[0,1,2,3,4,8,9,11]

Return false. There is no way to jump to the last stone as
the gap between the 5th and 6th stone is too large.

Solution

DP problem.

states:

dp[i][0]: k-1 steps
dp[i][1]: k steps
dp[i][2]: k+1 steps

Transitions:

for k in [-1, 0, 1]:
  dp[i+step][k+1] = max(dp[i+step][k+1], dp[i][j]+k)

Note: It has to use max function in the transition, because the later stones may update the jumps with lower distance.

class Solution:
    def canCross(self, stones: List[int]) -> bool:
        if len(stones) < 2:
            return True

        dp = defaultdict(lambda: [0, 0, 0])
        dp[1][1] = 1

        for i in stones[1:]:
            for j in range(3):
                if dp[i][j] == 0:
                    continue
                for k in [-1, 0, 1]:
                    step = dp[i][j] + k
                    if i+step == stones[-1]:
                        return True
                    elif step > 0 and i + step < stones[-1]:
                        dp[i+step][k+1] = max(dp[i+step][k+1], dp[i][j]+k)

        return False

376 - Wiggle Subsequence

leetcode

Problem

A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.

Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.

Example 1:

Input: [1,7,4,9,2,5]
Output: 6
Explanation: The entire sequence is a wiggle sequence.
Example 2:

Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
Explanation: There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].
Example 3:

Input: [1,2,3,4,5,6,7,8,9]
Output: 2
Follow up:
Can you do it in O(n) time?

Solution

class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
        if len(nums) < 2:
            return len(nums)

        first_diff = 1
        dp = [1] * len(nums)
        ret = 1

        for first_diff in [1, -1]:
            dp = [1] * len(nums)
            for i in range(1, len(nums)):
                if first_diff * (nums[i] - nums[i-1]) > 0:
                    dp[i] = dp[i-1] + 1
                    first_diff *= -1
                else:
                    dp[i] = dp[i-1]

            ret = max(ret, max(dp))

        return ret

86 - Partition List

leetcode

Problem

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

You should preserve the original relative order of the nodes in each of the two partitions.

Example:

Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5

Solution

Solution 1: Two Dummy Pointers

Simple version: just create two list, and append them in the end

class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        p1 = l1 = ListNode(0)
        p2 = l2 = ListNode(0)
        while head:
            if head.val < x:
                p1.next, p1 = head, head
            else:
                p2.next, p2 = head, head
            head.next, head = None, head.next

        p1.next = l2.next
        return l1.next

Solution 2: In place

Inplace swap with Two pointers:

p1: mark the end of smaller part

p2: go forward to find the smaller one and swap with p1.next

Addtional prev pointer for the swap.

  1. Use a dummy node to make it possible to swap the smaller one to the head
  2. Note the case when p1 == prev, no need to swap, just move p1 forward
class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        head = ListNode(0, head)

        p1, prev, p2 = head, head, head.next

        while p2:
            if p2.val < x:
                if p1 == prev:
                    prev, p2 = p2, p2.next
                else:
                    p1.next, p2.next, p2, prev.next = p2, p1.next, p2.next, p2.next
                p1 = p1.next
            else:
                prev, p2 = p2, p2.next

        return head.next

378 - Kth Smallest Element in a Sorted Matrix

leetcode

Problem

Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.

Note that it is the kth smallest element in the sorted order, not the kth distinct element.

Example:

matrix = [
   [ 1,  5,  9],
   [10, 11, 13],
   [12, 13, 15]
],
k = 8,

return 13.

Solution

Solution 1: Heap with marker

Maintain a heap, pop the first one, and push the right one and lower one into the heap.

But we need a mark matrix to tell if the one to be pushed was not visited before, since it may have been in the heap when we push the lower one, and now we come to this one from left to right.

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        if not matrix or not matrix[0]:
            return None
        n = len(matrix)
        heap = []
        heapq.heapify(heap)
        heapq.heappush(heap, (matrix[0][0], [0, 0]))
        mark = [[False] * n for _ in range(n)]
        while k > 0:
            ret = heapq.heappop(heap)
            i, j = ret[1]
            if i+1 < n and not mark[i+1][j]:
                heapq.heappush(heap, (matrix[i+1][j], [i+1, j]))
                mark[i+1][j] = True
            if j+1 < n and not mark[i][j+1]:
                heapq.heappush(heap, (matrix[i][j+1], [i, j+1]))
                mark[i][j+1] = True
            k -= 1

        return ret[0]

Solution 2: Heap without marker

We can eliminate the marker by push the first column into the heap at once. Then we only need to traverse from left to right.

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        n = len(matrix)
        heap = [(row[0], index, 0) for index, row in enumerate(matrix)]
        heapify(heap)
        while k > 0:
            ret, i, j = heapq.heappop(heap)
            if j+1 < n:
                heappush(heap, (matrix[i][j+1], i, j+1))
            k -= 1
        return ret
class Solution:
    def kthSmallest(self, matrix):
        lo, hi = len(matrix), len(matrix[0])
        while lo < hi:
            mid = (lo+hi)//2
            if sum(bisect.bisect_right(matrix[row], mid) for row in matrix) < k:
                lo = mid+1
            else:
                hi = mid

115 - Distinct Subsequences

leetcode

Problem

Given two strings s and t, return the number of distinct subsequences of s which equals t.

A string's subsequence is a new string formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (i.e., "ACE" is a subsequence of "ABCDE" while "AEC" is not).

It's guaranteed the answer fits on a 32-bit signed integer.



Example 1:

Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from S.
rabbbit
rabbbit
rabbbit
Example 2:

Input: s = "babgbag", t = "bag"
Output: 5
Explanation:
As shown below, there are 5 ways you can generate "bag" from S.
babgbag
babgbag
babgbag
babgbag
babgbag

Solution

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        m, n = len(s), len(t)
        dp = [[0] * (m+1) for _ in range(n+1)]

        for i in range(m+1):
            dp[0][i] = 1

        for i in range(1, n+1):
            for j in range(1, m+1):
                if s[j-1] == t[i-1]:
                    dp[i][j] = dp[i][j-1] + dp[i-1][j-1]
                else:
                    dp[i][j] = dp[i][j-1]

        return dp[n][m]

1392 - Longest Happy Prefix

leetcode

Problem

A string is called a happy prefix if is a non-empty prefix which is also a suffix (excluding itself).

Given a string s. Return the longest happy prefix of s .

Return an empty string if no such prefix exists.



Example 1:

Input: s = "level"
Output: "l"
Explanation: s contains 4 prefix excluding itself ("l", "le", "lev", "leve"), and suffix ("l", "el", "vel", "evel"). The largest prefix which is also suffix is given by "l".
Example 2:

Input: s = "ababab"
Output: "abab"
Explanation: "abab" is the largest prefix which is also suffix. They can overlap in the original string.
Example 3:

Input: s = "leetcodeleet"
Output: "leet"
Example 4:

Input: s = "a"
Output: ""


Constraints:

1 <= s.length <= 10^5
s contains only lowercase English letters.

Solution

class Solution:
    def longestPrefix(self, s: str) -> str:
        dp = [0] * len(s)
        j = 0
        i = 1
        while i < len(s):
            if s[i] == s[j]:
                j += 1
                dp[i] = j
            elif j > 0:
                j = dp[j-1]
                i -= 1
            i += 1
        return s[:dp[-1]]

395 - Longest Substring with At Least K Repeating Characters

leetcode

Problem

Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times.

Example 1:

Input:
s = "aaabb", k = 3

Output:
3

The longest substring is "aaa", as 'a' is repeated 3 times.
Example 2:

Input:
s = "ababbc", k = 2

Output:
5

The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times

Solution

class Solution:
    def longestSubstring(self, s: str, k: int) -> int:
        c = Counter(s)
        for i, v in c.items():
            if v < k:
                return max(self.longestSubstring(x, k) for x in s.split(i))
        return len(s)

127 - Word Ladder

leetcode

Problem

Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

Only one letter can be changed at a time.
Each transformed word must exist in the word list.
Note:

Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:

Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

Output: 5

Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Example 2:

Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: 0

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

Solution

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        if endWord not in wordList:
            return 0

        h = Counter(set(wordList))

        queue = [beginWord]
        ret = 1

        while queue:
            tmp = []
            while queue:
                w = queue.pop()
                if w == endWord:
                    return ret

                for i in range(len(w)):
                    for j in range(26):
                        to = w[:i] + chr(ord('a') + j) + w[i+1:]
                        if to != w and h[to]:
                            tmp.append(to)
                            h[to] = 0
            ret += 1
            queue = tmp
        return 0

126 - Word Ladder II

leetcode

Problem

Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

Only one letter can be changed at a time
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:

Return an empty list if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:

Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

Output:
[
  ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"]
]
Example 2:

Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: []

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

Solution

class Solution:
    def findLadders(self, beginWord: str, endWord: str, wordList: List[str]):
        if endWord not in wordList:
            return []

        h = Counter(set(wordList))
        paths = defaultdict(set)
        queue = [beginWord]
        h[beginWord] = 0
        levels = []
        seen = False
        while queue and not seen:
            tmp = []
            while queue:
                w = queue.pop()
                if w == endWord:
                    seen = True
                for i in range(len(w)):
                    for j in range(26):
                        to = w[:i] + chr(ord('a') + j) + w[i+1:]
                        if to != w and h[to]:
                            tmp.append(to)
                            paths[w].add(to)
            for t in tmp:
                h[t] = 0

            levels.append(list(tmp))
            queue = tmp

        ret = []
        self.dfs(beginWord, endWord, paths, [], ret)
        return ret

    def dfs(self, beginWord, endWord, paths, path, ret):
        if beginWord == endWord:
            ret.append(path+[beginWord])
            return

        for x in paths[beginWord]:
            self.dfs(x, endWord, paths, path+[beginWord], ret)

257 - Binary Tree Paths

leetcode

Problem

iven a binary tree, return all root-to-leaf paths.

Note: A leaf is a node with no children.

Example:

Input:

   1
 /   \
2     3
 \
  5

Output: ["1->2->5", "1->3"]

Explanation: All root-to-leaf paths are: 1->2->5, 1->3

Solution

class Solution:
    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        if not root:
            return []

        if not root.right and not root.left:
            return [str(root.val)]

        paths = [str(root.val) + "->" + s for s in self.binaryTreePaths(root.right) + self.binaryTreePaths(root.left)]
        return paths

691 - Stickers to Spell Word

leetcode

Problem

We are given N different types of stickers. Each sticker has a lowercase English word on it.

You would like to spell out the given target string by cutting individual letters from your collection of stickers and rearranging them.

You can use each sticker more than once if you want, and you have infinite quantities of each sticker.

What is the minimum number of stickers that you need to spell out the target? If the task is impossible, return -1.

Example 1:

Input:

["with", "example", "science"], "thehat"
Output:

3
Explanation:

We can use 2 "with" stickers, and 1 "example" sticker.
After cutting and rearrange the letters of those stickers, we can form the target "thehat".
Also, this is the minimum number of stickers necessary to form the target string.
Example 2:

Input:

["notice", "possible"], "basicbasic"
Output:

-1
Explanation:

We can't form the target "basicbasic" from cutting letters from the given stickers.
Note:

stickers has length in the range [1, 50].
stickers consists of lowercase English words (without apostrophes).
target has length in the range [1, 15], and consists of lowercase English letters.
In all test cases, all words were chosen randomly from the 1000 most common US English words, and the target was chosen as a concatenation of two random words.
The time limit may be more challenging than usual. It is expected that a 50 sticker test case can be solved within 35ms on average.

Solution

class Solution:
    def minStickers(self, stickers: List[str], target: str) -> int:
        self.ret = float("inf")
        self.stickers = stickers
        self.dfs([], target, 0, 0)
        return self.ret if self.ret < float("inf") else -1

    def dfs(self, pool, target, index, num_s):
        if num_s >= self.ret:
            return
        if index >= len(target):
            self.ret = num_s
            return
        if target[index] in pool:
            pool.remove(target[index])
            self.dfs(pool, target, index+1, num_s)
            pool.append(target[index])
        else:
            for w in self.stickers:
                if target[index] in w:
                    new_chars = list(w)
                    new_chars.remove(target[index])
                    self.dfs(pool + new_chars, target, index+1, num_s+1)

797 - All Paths From Source to Target

leetcode

Problem

Given a directed acyclic graph (DAG) of n nodes labeled from 0 to n - 1, find all possible paths from node 0 to node n - 1, and return them in any order.

The graph is given as follows: graph[i] is a list of all nodes you can visit from node i (i.e., there is a directed edge from node i to node graph[i][j]).

Solution

class Solution:
    def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:
        ret = []
        self.dfs(graph, 0, [0], ret)
        return ret

    def dfs(self, graph, source, path, ret):

        if source == len(graph) - 1:
            ret.append(list(path))

        for w in graph[source]:
            self.dfs(graph, w, path+[w], ret)

464 - Can I Win

leetcode

Problem

In the "100 game" two players take turns adding, to a running total, any integer from 1 to 10. The player who first causes the running total to reach or exceed 100 wins.

What if we change the game so that players cannot re-use integers?

For example, two players might take turns drawing from a common pool of numbers from 1 to 15 without replacement until they reach a total >= 100.

Given two integers maxChoosableInteger and desiredTotal, return true if the first player to move can force a win, otherwise return false. Assume both players play optimally.



Example 1:

Input: maxChoosableInteger = 10, desiredTotal = 11
Output: false
Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.
Example 2:

Input: maxChoosableInteger = 10, desiredTotal = 0
Output: true
Example 3:

Input: maxChoosableInteger = 10, desiredTotal = 1
Output: true


Constraints:

1 <= maxChoosableInteger <= 20
0 <= desiredTotal <= 300

Solution

DFS

Maintain a hashmap, use unchosen numbers as key,

class Solution:
    def canIWin(self, maxChoosableInteger: int, desiredTotal: int) -> bool:
        h = {}
        def canIWinRec(numbers, desiredTotal):
            if numbers[-1] >= desiredTotal:
                return True

            k = tuple(numbers)
            if k in h:
                return h[k]

            for i in range(len(numbers)):
                if not canIWinRec(numbers[:i] + numbers[i+1:], desiredTotal - numbers[i]):
                    h[k] = True
                    return True

            h[k] = False
            return False

        summed = (maxChoosableInteger + 1) * maxChoosableInteger / 2

        if summed < desiredTotal:
            return False

        if summed == desiredTotal:
            return maxChoosableInteger % 2

        numbers = list(range(1, maxChoosableInteger+1))
        return canIWinRec(numbers, desiredTotal)

116 - Populating Next Right Pointers in Each Node

leetcode

Problem

You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}
Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

Follow up:

You may only use constant extra space.
Recursive approach is fine, you may assume implicit stack space does not count as extra space for this problem.

Solution

Constant Space

class Solution:
    def connect(self, root: 'Node') -> 'Node':
        node = root
        while node:
            next_level = node.left
            while node and node.left:
                node.left.next = node.right
                node.right.next = node.next and node.next.left
                node = node.next
            node = next_level
        return root

295 - Find Median from Data Stream

leetcode

Problem

Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.

For example,
[2,3,4], the median is 3

[2,3], the median is (2 + 3) / 2 = 2.5

Design a data structure that supports the following two operations:

void addNum(int num) - Add a integer number from the data stream to the data structure.
double findMedian() - Return the median of all elements so far.


Example:

addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2


Follow up:

If all integer numbers from the stream are between 0 and 100, how would you optimize it?
If 99% of all integer numbers from the stream are between 0 and 100, how would you optimize it?

Solution

Two heaps, one for smaller half, one for larger half.

Follow ups:

  1. Keep a array for counting the numbers. Go through the array to find the median. It’s O(1).
  2. If 99% of all integer is between 0 and 100, keep the array. We need only count the ones over 100 and less than 0. Use the counts and the array to find the median
class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.small = []
        self.large = []

    def addNum(self, num: int) -> None:
        heappush(self.small, -heappushpop(self.large, num))
        if len(self.large) < len(self.small):
            heappush(self.large, -heappop(self.small))


    def findMedian(self) -> float:
        if not self.large:
            return 0
        if len(self.large) > len(self.small):
            return self.large[0]
        return (self.large[0] - self.small[0]) / 2

996 - Number of Squareful Arrays

leetcode

Problem

Given an array A of non-negative integers, the array is squareful if for every pair of adjacent elements, their sum is a perfect square.

Return the number of permutations of A that are squareful.  Two permutations A1 and A2 differ if and only if there is some index i such that A1[i] != A2[i].



Example 1:

Input: [1,17,8]
Output: 2
Explanation:
[1,8,17] and [17,8,1] are the valid permutations.
Example 2:

Input: [2,2,2]
Output: 1


Note:

1 <= A.length <= 12
0 <= A[i] <= 1e9

Solution

Simple DFS

class Solution:
    def numSquarefulPerms(self, A: List[int]) -> int:
        self.h = defaultdict(bool)

        mark = [False] * len(A)
        self.ret = 0
        self.dfs(sorted(A), [], mark)
        return self.ret

    def dfs(self, A, path, mark):

        if len(path) == len(A):
            self.ret += 1

        for i, v in enumerate(A):
            if (i > 0 and A[i] == A[i-1] and not mark[i-1]) or mark[i]:
                continue
            if not path or self.isSquare(path[-1]+v):
                mark[i] = True
                self.dfs(A, path+[v], mark)
                mark[i] = False

    def isSquare(self, num):
        return True if int((num**0.5))**2 == num else False

842 - Split Array into Fibonacci Sequence

leetcode

Problem

Given a string S of digits, such as S = "123456579", we can split it into a Fibonacci-like sequence [123, 456, 579].

Formally, a Fibonacci-like sequence is a list F of non-negative integers such that:

0 <= F[i] <= 2^31 - 1, (that is, each integer fits a 32-bit signed integer type);
F.length >= 3;
and F[i] + F[i+1] = F[i+2] for all 0 <= i < F.length - 2.
Also, note that when splitting the string into pieces, each piece must not have extra leading zeroes, except if the piece is the number 0 itself.

Return any Fibonacci-like sequence split from S, or return [] if it cannot be done.

Example 1:

Input: "123456579"
Output: [123,456,579]
Example 2:

Input: "11235813"
Output: [1,1,2,3,5,8,13]
Example 3:

Input: "112358130"
Output: []
Explanation: The task is impossible.
Example 4:

Input: "0123"
Output: []
Explanation: Leading zeroes are not allowed, so "01", "2", "3" is not valid.
Example 5:

Input: "1101111"
Output: [110, 1, 111]
Explanation: The output [11, 0, 11, 11] would also be accepted.
Note:

1 <= S.length <= 200
S contains only digits.

Solution

class Solution:
    def splitIntoFibonacci(self, S: str) -> List[int]:
        self.upper_bound = 2**31-1
        self.ret = []

        self.dfs(S, 0, [], [])

        return self.ret

    def dfs(self, S, index, ret, path):
        if self.ret:
            return

        if index == len(S) and "".join(map(str, ret)) == S and len(ret) >= 3:
            self.ret = list(ret)
            return
        elif index == len(S):
            return

        path.append(S[index])

        n = int("".join(path))
        if self.isFibonacci(ret, n):
            self.dfs(S, index+1, ret+[n], [])

        self.dfs(S, index+1, ret, path)
        path.pop()

    def isFibonacci(self, sequence, number):
        if number < 0 or number > self.upper_bound:
            return False

        if len(sequence) < 2:
            return True

        return True if number == sequence[-1] + sequence[-2] else False

486 - Predict the Winner

leetcode

Problem

Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on.
Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins.

Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score.

Example 1:

Input: [1, 5, 2]
Output: False
Explanation: Initially, player 1 can choose between 1 and 2.
If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2).
So, final score of player 1 is 1 + 2 = 3, and player 2 is 5.
Hence, player 1 will never be the winner and you need to return False.


Example 2:

Input: [1, 5, 233, 7]
Output: True
Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233.
Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win.


Constraints:

1 <= length of the array <= 20.
Any scores in the given array are non-negative integers and will not exceed 10,000,000.
If the scores of both players are equal, then player 1 is still the winner.

Solution

class Solution:
    def PredictTheWinner(self, nums: List[int]) -> bool:
        dp = {}
        def getMaxDiff(left, right):
            if (left, right) not in dp:
                if left == right:
                    return nums[left]

                dp[left, right] = max(nums[left] - getMaxDiff(left+1, right), nums[right] - getMaxDiff(left, right-1))
            return dp[left, right]

        return getMaxDiff(0, len(nums)-1) >= 0

556 - Next Greater Element III

leetcode

Problem

Given a positive 32-bit integer n, you need to find the smallest 32-bit integer which has exactly the same digits existing in the integer n and is greater in value than n. If no such positive 32-bit integer exists, you need to return -1.

Example 1:

Input: 12
Output: 21


Example 2:

Input: 21
Output: -1

Solution

class Solution:
    def nextGreaterElement(self, n: int) -> int:
        s = list(str(n))

        first = len(s) - 1
        for i in range(len(s)-2, -1, -1):
            if s[i] < s[i+1]:
                first = i
                break

        if first == len(s) - 1:
            return -1

        second = first+1
        minimum = s[second]

        for i in range(first+1, len(s)):
            if s[i] < minimum and s[i] > s[first]:
                minumum = s[i]
                second = i

        s[first], s[second] = s[second], s[first]

        ret = int("".join(s[:first+1] + sorted(s[first+1:])))
        return ret if ret <= (1<<31-1) else -1

381 - Insert Delete GetRandom O(1) - Duplicates allowed

leetcode

Problem

Design a data structure that supports all following operations in average O(1) time.

Note: Duplicate elements are allowed.
insert(val): Inserts an item val to the collection.
remove(val): Removes an item val from the collection if present.
getRandom: Returns a random element from current collection of elements. The probability of each element being returned is linearly related to the number of same value the collection contains.
Example:

// Init an empty collection.
RandomizedCollection collection = new RandomizedCollection();

// Inserts 1 to the collection. Returns true as the collection did not contain 1.
collection.insert(1);

// Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1].
collection.insert(1);

// Inserts 2 to the collection, returns true. Collection now contains [1,1,2].
collection.insert(2);

// getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3.
collection.getRandom();

// Removes 1 from the collection, returns true. Collection now contains [1,2].
collection.remove(1);

// getRandom should return 1 and 2 both equally likely.
collection.getRandom();

Solution

Notes:

Remove is tricky.

  1. Find the index of the element to remove in Hash.
  2. Replace the element with the last element in array, and pop the last one.
  3. Don’t forget to update the index of copied element in Hash.
  4. You have to firstly add the new index and then delete. There is a corner case when there is only one element. If you delete it first and then add, the same index is added back to hash.
class RandomizedCollection:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.list = []
        self.dict = defaultdict(set)

    def insert(self, val: int) -> bool:
        """
        Inserts a value to the collection. Returns true if the collection did not already contain the specified element.
        """
        self.list.append(val)
        if val not in self.dict:
            ret = True
        else:
            ret = False
        self.dict[val].add(len(self.list) - 1)
        return ret


    def remove(self, val: int) -> bool:
        """
        Removes a value from the collection. Returns true if the collection contained the specified element.
        """
        if self.dict[val]:
            rm_index = self.dict[val].pop()
            last = self.list[-1]
            self.list[rm_index] = last
            self.dict[last].add(rm_index)
            self.dict[last].discard(len(self.list) - 1)
            self.list.pop()
            return True
        else:
            return False

    def getRandom(self) -> int:
        """
        Get a random element from the collection.
        """
        return random.choice(self.list)


# Your RandomizedCollection object will be instantiated and called as such:
# obj = RandomizedCollection()
# param_1 = obj.insert(val)
# param_2 = obj.remove(val)
# param_3 = obj.getRandom()

134 - Gas Station

leetcode

Problem

There are N gas stations along a circular route, where the amount of gas at station i is gas[i].

You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.

Return the starting gas station's index if you can travel around the circuit once in the clockwise direction, otherwise return -1.

Note:

If there exists a solution, it is guaranteed to be unique.
Both input arrays are non-empty and have the same length.
Each element in the input arrays is a non-negative integer.
Example 1:

Input:
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

Output: 3

Explanation:
Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 4. Your tank = 4 - 1 + 5 = 8
Travel to station 0. Your tank = 8 - 2 + 1 = 7
Travel to station 1. Your tank = 7 - 3 + 2 = 6
Travel to station 2. Your tank = 6 - 4 + 3 = 5
Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3.
Therefore, return 3 as the starting index.
Example 2:

Input:
gas  = [2,3,4]
cost = [3,4,3]

Output: -1

Explanation:
You can't start at station 0 or 1, as there is not enough gas to travel to the next station.
Let's start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 0. Your tank = 4 - 3 + 2 = 3
Travel to station 1. Your tank = 3 - 3 + 3 = 3
You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3.
Therefore, you can't travel around the circuit once no matter where you start.

Solution

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        start = gap = left = 0

        for i in range(len(gas)):
            left += gas[i] - cost[i]
            if left < 0:
                start = i+1
                gap += left
                left = 0

        return start if left+gap >= 0 else -1

135 - Candy

leetcode

Problem

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

Each child must have at least one candy.
Children with a higher rating get more candies than their neighbors.
What is the minimum candies you must give?

Example 1:

Input: [1,0,2]
Output: 5
Explanation: You can allocate to the first, second and third child with 2, 1, 2 candies respectively.
Example 2:

Input: [1,2,2]
Output: 4
Explanation: You can allocate to the first, second and third child with 1, 2, 1 candies respectively.
             The third child gets 1 candy because it satisfies the above two conditions.

Solution

class Solution:
    def candy(self, ratings: List[int]) -> int:

        candy = [1] * len(ratings)

        for i in range(len(candy)-1):
            if ratings[i+1] > ratings[i]:
                candy[i+1] = candy[i] + 1

        for i in range(len(candy)-1, 0, -1):
            if ratings[i-1] > ratings[i]:
                candy[i-1] = max(candy[i]+1, candy[i-1])

        return sum(candy)

140 - Word Break II

leetcode

Problem

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.

Note:

The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example 1:

Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
  "cats and dog",
  "cat sand dog"
]
Example 2:

Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
  "pine apple pen apple",
  "pineapple pen apple",
  "pine applepen apple"
]
Explanation: Note that you are allowed to reuse a dictionary word.
Example 3:

Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]

Solution

class Solution(object):
    def wordBreak(self, s, wordDict):
        return self.dfs(s, set(wordDict), 0, {})

    def dfs(self, s, wordDict, start, memo):

        if start >= len(s): return [""]
        if start in memo: return memo[start]

        ans = []
        for i in range(start+1, len(s)+1):
            w = s[start:i]
            if w in wordDict:
                ans += [" ".join([w, x]) if x else w for x in self.dfs(s, wordDict, i, memo)]

        memo[start] = ans
        return ans

139 - Word Break

leetcode

Problem

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Note:

The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example 1:

Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
Example 2:

Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
             Note that you are allowed to reuse a dictionary word.
Example 3:

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false

Solution

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        memo = {}

        def dfs(index):
            if index in memo: return memo[index]
            if index >= len(s): return True

            ret = False
            for i in range(index+1, len(s)+1):
                if s[index:i] in wordDict:
                    if dfs(i):
                        ret = True
                        break

            memo[index] = ret
            return ret

        return dfs(0)

443 - String Compression

leetcode

Problem

Given an array of characters chars, compress it using the following algorithm:

Begin with an empty string s. For each group of consecutive repeating characters in chars:

If the group's length is 1, append the character to s.
Otherwise, append the character followed by the group's length.
The compressed string s should not be returned separately, but instead be stored in the input character array chars. Note that group lengths that are 10 or longer will be split into multiple characters in chars.

After you are done modifying the input array, return the new length of the array.


Follow up:
Could you solve it using only O(1) extra space?



Example 1:

Input: chars = ["a","a","b","b","c","c","c"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"]
Explanation: The groups are "aa", "bb", and "ccc". This compresses to "a2b2c3".
Example 2:

Input: chars = ["a"]
Output: Return 1, and the first character of the input array should be: ["a"]
Explanation: The only group is "a", which remains uncompressed since it's a single character.
Example 3:

Input: chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
Output: Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"].
Explanation: The groups are "a" and "bbbbbbbbbbbb". This compresses to "ab12".
Example 4:

Input: chars = ["a","a","a","b","b","a","a"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","3","b","2","a","2"].
Explanation: The groups are "aaa", "bb", and "aa". This compresses to "a3b2a2". Note that each group is independent even if two groups have the same character.


Constraints:

1 <= chars.length <= 2000
chars[i] is a lower-case English letter, upper-case English letter, digit, or symbol.

Solution

class Solution:
    def compress(self, chars: List[str]) -> int:
        if len(chars) < 2: return len(chars)

        read, write = 1, 0
        count = 1

        while read < len(chars):
            if chars[read] == chars[read-1]:
                count += 1
            elif count > 1:
                chars[write] = chars[read-1]
                for s in list(str(count)):
                    write += 1
                    chars[write] = s
                write += 1
                count = 1
            else:
                chars[write] = chars[read-1]
                write += 1
            read += 1

        if count == 1:
            chars[write] = chars[read-1]
            write += 1
        else:
            chars[write] = chars[read-1]
            for s in list(str(count)):
                write += 1
                chars[write] = s
            write += 1

        return write

30 - Substring with Concatenation of All Words

leetcode

Problem

You are given a string s and an array of strings words of the same length. Return all starting indices of substring(s) in s that is a concatenation of each word in words exactly once, in any order, and without any intervening characters.

You can return the answer in any order.



Example 1:

Input: s = "barfoothefoobarman", words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoo" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.
Example 2:

Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
Output: []
Example 3:

Input: s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
Output: [6,9,12]

Solution

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []
        length = len(words[0])
        total_words = len(words)
        words = Counter(words)
        ans = []
        def dfs(start, i, path):
            if len(path) == total_words:
                ans.append(start)
                return
            if i >= len(s):
                return
            w = s[i:i+length]
            if words[w]:
                words[w] -= 1
                dfs(start, i+length, path+[w])
                words[w] += 1

        for i in range(len(s)-length*total_words+1):
            dfs(i, i, [])

        return ans

1010 - Pairs of Songs With Total Durations Divisible by 60

leetcode

Problem

You are given a list of songs where the ith song has a duration of time[i] seconds.

Return the number of pairs of songs for which their total duration in seconds is divisible by 60. Formally, we want the number of indices i, j such that i < j with (time[i] + time[j]) % 60 == 0.



Example 1:

Input: time = [30,20,150,100,40]
Output: 3
Explanation: Three pairs have a total duration divisible by 60:
(time[0] = 30, time[2] = 150): total duration 180
(time[1] = 20, time[3] = 100): total duration 120
(time[1] = 20, time[4] = 40): total duration 60
Example 2:

Input: time = [60,60,60]
Output: 3
Explanation: All three pairs have a total duration of 120, which is divisible by 60.


Constraints:

1 <= time.length <= 6 * 104
1 <= time[i] <= 500

Solution

class Solution:
    def numPairsDivisibleBy60(self, time: List[int]) -> int:
        counter = Counter()
        ans = 0
        for t in time:
            ans += counter[(60 - t%60)%60]
            counter[t%60] += 1
        return ans

235 - Lowest Common Ancestor of a Binary Search Tree

leetcode

Problem

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.

Solution

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        while root and (p.val-root.val) * (q.val-root.val) > 0:
            if p.val - root.val < 0:
                root = root.left
            else:
                root = root.right
        return root

701 - Insert into a Binary Search Tree

leetcode

Problem

You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST.

Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them.

Solution

class Solution:
    def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
        if not root:
            return TreeNode(val)
        if val > root.val:
            root.right = self.insertIntoBST(root.right, val)
        else:
            root.left = self.insertIntoBST(root.left, val)

        return root

323 - Number of Connected Components in an Undirected Graph

leetcode

Problem

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to find the number of connected components in an undirected graph.
Example 1:
Input: n = 5 and edges = [[0, 1], [1, 2], [3, 4]]
     0          3
     |          |
     1 --- 2    4
Output: 2
Example 2:
Input: n = 5 and edges = [[0, 1], [1, 2], [2, 3], [3, 4]]
     0           4
     |           |
     1 --- 2 --- 3
Output:  1
Note:
You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Solution

from collections import defaultdict

class Solution():
    def ConnectedComponents(self, edges):
      graph = defaultdict(list)
      marked = {}
      for edge in edges:
          graph[edge[0]].append(edge[1])
          graph[edge[1]].append(edge[0])
          marked[edge[0]] = False
          marked[edge[1]] = False

      ans = 0
      for v in graph.keys():
          if not marked[v]:
              self.dfs(graph, v, marked)
              ans += 1
      return ans

    def dfs(self, graph, v, marked):
        marked[v] = True
        for w in graph[v]:
            if not marked[w]:
                self.dfs(graph, w, marked)

print(Solution().ConnectedComponents([[0, 1], [1, 2], [3, 4]]))

863 - All Nodes Distance K in Binary Tree

leetcode

Problem

We are given a binary tree (with root node root), a target node, and an integer value K.

Return a list of the values of all nodes that have a distance K from the target node.  The answer can be returned in any order.



Example 1:

Input: root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2

Output: [7,4,1]

Explanation:
The nodes that are a distance 2 from the target node (with value 5)
have values 7, 4, and 1.

Note:

1. The given tree is non-empty.
2. Each node in the tree has unique values 0 <= node.val <= 500.
3. The target node is a node in the tree.
4. 0 <= K <= 1000.

Solution

BFS

  1. Build a graph. Connet the parent and child in both directions.
  2. Start BFS search from the target value, with step of K.

In K step, the element in the new level are the answer.

class Solution:
    def distanceK(self, root: TreeNode, target: TreeNode, K: int) -> List[int]:

        graph = defaultdict(list)

        self.connect(graph, root, root.left)
        self.connect(graph, root, root.right)
        level = [target.val]
        marked = set(level)
        for i in range(K):
            new_level = []
            for x in level:
                for w in graph[x]:
                    if w not in marked:
                        new_level.append(w)
            level = new_level
            marked |= set(new_level)

        return level

    def connect(self, graph, parent, child):
        if parent and child:
            graph[parent.val].append(child.val)
            graph[child.val].append(parent.val)

            self.connect(graph, child, child.left)
            self.connect(graph, child, child.right)

515 - Find Largest Value in Each Tree Row

leetcode

Problem

Given the root of a binary tree, return an array of the largest value in each row of the tree (0-indexed).

Solution

class Solution:
    def largestValues(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        ans = []
        level = [root]
        seen = set(level)

        while level:
            new_level = []
            max_val = -float('inf')
            for node in level:
                if node:
                    if node.left:
                        new_level.append(node.left)
                    if node.right:
                        new_level.append(node.right)
                    max_val = max(max_val, node.val)
            ans.append(max_val)
            level = new_level

        return ans

42 - Trapping Rain Water

leetcode

Problem

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.


Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped.

Solution

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        leftHeight = self.leftMaxHeight(height)
        rightHeight = self.rightMaxHeight(height)

        for i in range(len(height)):
            max_bar = min(leftHeight[i], rightHeight[i])
            if max_bar > height[i]:
                ans += max_bar - height[i]
        return ans

    def leftMaxHeight(self, height):
        ans = [0] * len(height)
        max_left = 0
        for i in range(len(height)):
            ans[i] = max_left
            max_left = max(height[i], max_left)
        return ans

    def rightMaxHeight(self, height):
        ans = [0] * len(height)
        max_right = 0
        for i in range(len(height)-1, -1, -1):
            ans[i] = max_right
            max_right = max(height[i], max_right)
        return ans

236 - Lowest Common Ancestor of a Binary Tree

leetcode

Problem

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”

Solution

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root:
            return None

        if root.val == p.val or root.val == q.val:
            return root

        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)

        if not left:
            return right
        elif not right:
            return left
        else:
            return root