Backtracking Flashcards

1
Q
  1. Generate Parentheses
    Solved
    Medium

Topics

Companies Amazon
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Example 1:

Input: n = 3
Output: [”((()))”,”(()())”,”(())()”,”()(())”,”()()()”]
Example 2:

Input: n = 1
Output: [”()”]

Constraints:

1 <= n <= 8

https://leetcode.com/problems/generate-parentheses/description/

A

Trick: Add open when you can, add closed when you can
We do not need to pop stuff as we do not modify the original value, we just send a modified version to the next call.

class Solution(object):
def generateParenthesis(self, n):
“””
:type n: int
:rtype: List[str]
“””
ret = []
def rec(val, numopen, numclosed):
if numopen == numclosed == n:
ret.append(““.join(val))
return

        if numopen < n:
            rec(val + ['('], numopen+1, numclosed)
        if numclosed < numopen:
            rec(val + [')'], numopen, numclosed+1)

    rec([], 0, 0)
    return ret
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q
  1. Subsets
    Medium
    Given an integer array nums of unique elements, return all possible
    subsets
    (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

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

Input: nums = [0]
Output: [[],[0]]

Constraints:

1 <= nums.length <= 10
-10 <= nums[i] <= 10
All the numbers of nums are unique.

A

class Solution(object):
def subsets(self, nums):
“””
:type nums: List[int]
:rtype: List[List[int]]
“””
output = [[]]

    for num in nums:
        output += [cur + [num] for cur in output]
    return output

Backtracking
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
subsets = []

    def dfs(i):
        if i >= len(nums):
            res.append(subsets.copy())
            return
        subsets.append(nums[i])
        dfs(i+1)
        subsets.pop()
        dfs(i+1)
    dfs(0)
    return res
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q
  1. Combination Sum
    Medium
    Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.

The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the
frequency
of at least one of the chosen numbers is different.

The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

Example 1:

Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.
Example 2:

Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]
Example 3:

Input: candidates = [2], target = 1
Output: []

Constraints:

1 <= candidates.length <= 30
2 <= candidates[i] <= 40
All elements of candidates are distinct.
1 <= target <= 40

https://leetcode.com/problems/combination-sum/description/

A

class Solution(object):
def combinationSum(self, candidates, target):
“””
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
“””

    output = []
    def rec(nums, idx):
        if nums and sum(nums) == target:
            output.append(nums[:])
            return
        if (idx == len(candidates)) or (nums and sum(nums) > target):
            return
        rec([candidates[idx]] + nums, idx)
        rec(nums, idx + 1)
    rec([], 0)
    return output
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q
  1. Permutations
    Medium
    Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

Example 1:

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

Input: nums = [0,1]
Output: [[0,1],[1,0]]
Example 3:

Input: nums = [1]
Output: [[1]]

Constraints:

1 <= nums.length <= 6
-10 <= nums[i] <= 10
All the integers of nums are unique.

https://leetcode.com/problems/permutations/description/

A

Note the use of Set here, otherwise for each call we have to do n operations.
~~~
class Solution(object):
def permute(self, nums):
“””
:type nums: List[int]
:rtype: List[List[int]]
“””

    output = []
    seen = set()
    def rec(perms):
        if len(perms) == len(nums):
            output.append(perms[:])
            return
        for num in nums:
            if num not in seen:
                seen.add(num)
                rec([num] + perms)
                seen.remove(num)
    rec([])
    return output ```				
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        output = [[]]
        for n in nums:
            temp = []
            for p in output:
                for i in range(len(p)+1):
                    temp.append(p[:i] + [n] + p[i:])
            output = temp
        return output
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q
  1. Permutations II
    Solved
    Medium
    Topics
    Companies
    Given a collection of numbers, nums, that might contain duplicates, return all possible unique permutations in any order.

Example 1:

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

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

Constraints:

1 <= nums.length <= 8
-10 <= nums[i] <= 10

https://leetcode.com/problems/permutations-ii/description/

A

from collections import Counter
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
output = []
counts = Counter(nums)
def rec(perms):
if len(perms) == len(nums):
output.append(perms[:])
return

        # Use the unique nums to iterate. This way you accurately track if its been used or not. 
        for num in counts.keys():
            if counts[num]:
                counts[num] -= 1
                rec([num] + perms)
                counts[num] += 1
    rec([])
    return output
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q
  1. Subsets II
    Solved
    Medium
    Topics
    Companies
    Given an integer array nums that may contain duplicates, return all possible
    subsets
    (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

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

Input: nums = [0]
Output: [[],[0]]

Constraints:

1 <= nums.length <= 10
-10 <= nums[i] <= 10

https://leetcode.com/problems/subsets-ii/description/

A

The non sort does the same thing, if we notice, we do not make a call without the number if it’s already in the set.
We know that if it’s already in the set, the first occurance, will make the call without itself.
This will make sure to include all subsets.
~~~
et 1123
1 1 2 3
1 1 2
1 1 3
1 2– skip this chain as it will be repeated later.
1
1 2 3 - here is the chain we skipped earlier.
1 2
1 3
1
2 3
2
3
~~~

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

        def rec(subsets, idx):
            if idx == len(nums):
                output.append(subsets[:])
                return 
            rec([nums[idx]] + subsets, idx + 1)
            if nums[idx] not in subsets:
                rec(subsets, idx + 1)
        rec([], 0)
        return output

Other way is to sort the nums and if the previous is the same, skip.
add subsets so far each iteration to the output.
Each call go over the all the nums from idx to len(nums) where idx is incremented for each call.
Only the first non dup number will be used if its not the first time using it (i != idx)
eg.
~~~
1123
for ease of example first 1 is 1a and secondis 1b
1a
1a 1b
1a 1b 2
1a 1b 2 3
1a 1b 3
1a 1b
1a 2
1a 2 3
1a 3
1b - Skip as 1b is not idx (0) and its == 1a (i-1 loc)
2 3
2
3
~~~

class Solution(object):
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        output = []
        nums.sort()
        def rec(subsets, idx):
            output.append(subsets[:])
            if idx == len(nums):
                return
            for i in range(idx, len(nums)):
                if i != idx and nums[i-1] == nums[i]:
                    continue
                rec(subsets + [nums[i]], i+1)
        rec([], 0)
        return output
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q
  1. Word Search
    Solved
    Medium
    Topics
    Companies
    Given an m x n grid of characters board and a string word, return true if word exists in the grid.

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

Example 1:

Input: board = [[“A”,”B”,”C”,”E”],[“S”,”F”,”C”,”S”],[“A”,”D”,”E”,”E”]], word = “ABCCED”
Output: true
Example 2:

Input: board = [[“A”,”B”,”C”,”E”],[“S”,”F”,”C”,”S”],[“A”,”D”,”E”,”E”]], word = “SEE”
Output: true
Example 3:

Input: board = [[“A”,”B”,”C”,”E”],[“S”,”F”,”C”,”S”],[“A”,”D”,”E”,”E”]], word = “ABCB”
Output: false

Constraints:

m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board and word consists of only lowercase and uppercase English letters.

Follow up: Could you use search pruning to make your solution faster with a larger board?

https://leetcode.com/problems/word-search/

A
from collections import deque, Counter

class Solution(object):
    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        movements = [[0,1], [-1,0], [1,0], [0,-1]]
        seen = set()
        rows = len(board)
        cols = len(board[0])
        # If matrix smaller than word
        if rows * cols < len(word):
            return False
        chars = {c for row in board for c in row}
        # If board does not have all the characters in the word
        if not chars.issuperset(set(word)):
            return False
        # if the board char counts are not same as word's char count
        board_counts = Counter(c for row in board for c in row)
        counts_word = Counter(word)
        if not counts_word <= board_counts:
            return False
        word = word if counts_word[word[0]] < counts_word[word[-1]] else word[::-1] 
        
        def dfs(ri, ci, word):
            if not word:
                return True
            seen.add((ri,ci))
            for rm, cm in movements:
                rn, cn = ri + rm, ci + cm
                if rn in range(rows) and cn in range(cols) and board[rn][cn] == word[0] and (rn, cn) not in seen:
                    found = dfs(rn, cn, word[1:])
                    if found:
                        return True
            seen.remove((ri, ci))
                    
            return False

        for r in range(rows):
            for c in range(cols):
                if board[r][c] == word[0]:
                    found = dfs(r,c, word[1:])
                    if found:
                        return True
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q
  1. Palindrome Partitioning
    Medium

Given a string s, partition s such that every
substring
of the partition is a
palindrome
. Return all possible palindrome partitioning of s.

Example 1:

Input: s = “aab”
Output: [[“a”,”a”,”b”],[“aa”,”b”]]

Example 2:

Input: s = “a”
Output: [[“a”]]

Constraints:

1 <= s.length <= 16
s contains only lowercase English letters.

https://leetcode.com/problems/palindrome-partitioning/description/

A
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        output = []

        # Slower
        def _isPalindrome(word):
            l = 0
            r = len(word) - 1 
            while l < r:
                if word[l] != word[r]:
                    return False
                l+=1
                r-=1
            return True

        # This is faster, possibly optimized by python.
        def isPalindrome(word):
            return word == word[::-1]
        
        def rec(i, palins):
            if i == len(s):
                output.append(palins[:])
                return
            for idx in range(i, len(s)):
                if isPalindrome(s[i:idx+1]):
                    rec(idx+1, palins + [s[i:idx+1]])
        rec(0, [])
        return output

Using Dynamic Programming

class Solution:
    def partition(self, s):
        res = []
        dp = [[False] * len(s) for _ in range(len(s))]
        
        def dfs(pals, start):
            if start >= len(s):
                res.append(pals[:])
                return
            for end in range(start, len(s)):
                if s[start] == s[end] and (end-start <= 2 or dp[start+1][end-1]):
                    dp[start][end] = True
                    dfs(pals + [s[start:end+1]], end+1)
        dfs([], 0)
        return res
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q
  1. Letter Combinations of a Phone Number
    Solved
    Medium
    Topics
    Companies

Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order.

A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Example 1:

Input: digits = “23”
Output: [“ad”,”ae”,”af”,”bd”,”be”,”bf”,”cd”,”ce”,”cf”]

Example 2:

Input: digits = “”
Output: []

Example 3:

Input: digits = “2”
Output: [“a”,”b”,”c”]

Constraints:

0 <= digits.length <= 4
digits[i] is a digit in the range ['2', '9'].

https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/

A

class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits:
return []
letter_map = {
“2”: “abc”,
“3”: “def”,
“4”: “ghi”,
“5”: “jkl”,
“6”: “mno”,
“7”: “pqrs”,
“8”: “tuv”,
“9”: “wxyz”
}
output = []
def build(i, comb):
if i == len(digits):
output.append(comb[:])
return
for c in letter_map[digits[i]]:
build(i+1, comb+c)
build(0, “”)
return output

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q
  1. N-Queens
    Solved
    Hard
    Topics
    Companies

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle. You may return the answer in any order.

Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space, respectively.

Example 1:

Input: n = 4
Output: [[“.Q..”,”…Q”,”Q…”,”..Q.”],[”..Q.”,”Q…”,”…Q”,”.Q..”]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above

Example 2:

Input: n = 1
Output: [[“Q”]]

Constraints:

1 <= n <= 9

https://leetcode.com/problems/n-queens/description/

A
Trick to know if pos or neg diagonals are occupied
## neg Diag goes from top left to bottom right, in this case along the diag r-c is equal
## Pos diag goes from bottom left to top right in this case along the diag r+c is equal 
## For each queen, place it at every colum in the given row to see if its valid position,
## if it is, then call backtrack to pass it on to the next queen and row (each queen will have it's own row)

from collections import defaultdict
class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        b = [["." for i in range(n)] for j in range(n)]
        occupied_row = set()
        occupied_col = set()
        occupied_lr = set() # r-c
        occupied_rl = set() # r+c
        movements = [[0,1], [1,0], [-1,0], [0, -1]]
        res = []

        def isValid(r,c):
            if (r in occupied_row or c in occupied_col or r-c in occupied_lr or r+c in occupied_rl):
                return False
            return True

        def dfs(row, queens, board):
            if row == n:
                res.append(["".join(row) for row in board])
                return
            for col in range(n):
                if (row in range(n) and col in range(n) and isValid(row, col)):
                    occupied_row.add(row)
                    occupied_col.add(col)
                    occupied_lr.add(row-col)
                    occupied_rl.add(row+col)
                    board[row][col] = "Q"
                    dfs(row+1, queens, board[:])
                    occupied_row.remove(row)
                    occupied_col.remove(col)
                    occupied_lr.remove(row-col)
                    occupied_rl.remove(row+col)
                    board[row][col] = "."
            return

        dfs(0, 0, b[:])
        return res
How well did you know this?
1
Not at all
2
3
4
5
Perfectly