Showing posts with label backtracking. Show all posts
Showing posts with label backtracking. Show all posts

Wednesday, May 29, 2024

[LeetCode] N-Queens II

Problem: 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 the number of distinct solutions to the n-queens puzzle.

Example:


Input: n = 4
Output: 2
Explanation: There are two distinct solutions to the 4-queens puzzle as shown.
Input: n = 1
Output: 1

Constraints:

  • 1 <= n <= 9


Approach: This is classic backtracking problem. We will take a boolean 2D array of size n x n and we treat it like a chess board. Now at reach row starting from 0 to n in order, we will try to place queen at every possible column. When we see we can place queen at the last row safely, we can increase the number of solution by 1.

We can reduce the space by just taking 3 arrays one for columns of size n, and 2 for two diagonals of size 2n but as you see in the constraint n is always less than or equal to 9, we don't have to worry about it and make our solution complex.


Implementation in C#:

    public int TotalNQueens(int n)
    {
        if (n <= 1)
        {
            return n;
        }
        bool[,] board = new bool[n,n];
        return this.TotalNQueens(board, 0, n);
    }

    private int TotalNQueens(bool[,] board, int currRow, int n)
    {
        if (currRow == n)
        {
            return 1;
        }
        int result = 0;
        for (int i = 0; i < n; ++i)
        {
            if (this.CanPlaceQueen(board, currRow, i, n))
            {
                board[currRow, i] = true;
                result += this.TotalNQueens(board, currRow + 1, n);
                board[currRow, i] = false;
            }
        }
        return result;
    }

    private bool CanPlaceQueen(bool[,] board, int row, int col, int n)
    {
        for (int i = 0; i < n; ++i)
        {
            if (board[i,col])
            {
                return false;
            }
        }
        int r = row, c = col;
        while (r >= 0 && c >= 0)
        {
            if (board[r, c])
            {
                return false;
            }
            --r;
            --c;
        }
        r = row;
        c = col;
        while (r >= 0 && c < n)
        {
            if (board[r, c])
            {
                return false;
            }
            --r;
            ++c;
        }
        return true;
    }

Complexity: O(n * !n)

Thursday, December 22, 2022

[LeetCode] Combination Sum II

Problem: Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target.

Each number in candidates may only be used once in the combination.

Note: The solution set must not contain duplicate combinations.

Example:

Input: candidates = [10,1,2,7,6,1,5], target = 8
Output: 
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
Input: candidates = [2,5,2,1,2], target = 5
Output: 
[
[1,2,2],
[5]
]


Approach: Its a typical backtracking problem except we need to take care of the duplicates too. We can take care of duplicates by sorting the array "candidates".


Implementation in C#:

    public IList<IList<int>> CombinationSum2(int[] candidates, int target)
    {
        List<IList<int>> result = new List<IList<int>>();
        Array.Sort(candidates);
        List<int> currCandidates = new List<int>();
        this.CombinationSum2Rec(candidates,
                            0,
                            target,
                            0,
                            currCandidates,
                            result);
        return result;
    }

    private void CombinationSum2Rec(int[] candidates,
                                int currIndex,
                                int target,
                                int currSum,
                                List<int> currCandidates,
                                List<IList<int>> result)
    {
        if (currSum == target)
        {
            result.Add(new List<int>(currCandidates));
            return;
        }
        for (int i = currIndex; i < candidates.Length; ++i)
        {
            if (currSum + candidates[i] > target)
            {
                break;
            }
            if (i > currIndex && candidates[i] ==  candidates[i - 1])
            {
                continue;
            }
            currCandidates.Add(candidates[i]);
            this.CombinationSum2Rec(candidates,
                                i + 1,
                                target,
                                currSum + candidates[i],
                                currCandidates,
                                result);
            currCandidates.RemoveAt(currCandidates.Count - 1);
        }
    }

Complexity: O(2^n)

Monday, June 27, 2022

[LeetCode] Combination Sum III

Problem: Find all valid combinations of k numbers that sum up to n such that the following conditions are true:

  • Only numbers 1 through 9 are used.
  • Each number is used at most once.

Return a list of all possible valid combinations. The list must not contain the same combination twice, and the combinations may be returned in any order.

Example:

Input: k = 3, n = 7
Output: [[1,2,4]]
Explanation:
1 + 2 + 4 = 7
There are no other valid combinations.
Input: k = 3, n = 9
Output: [[1,2,6],[1,3,5],[2,3,4]]
Explanation:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
There are no other valid combinations.
Input: k = 4, n = 1
Output: []
Explanation: There are no valid combinations.
Using 4 different numbers in the range [1,9], the smallest sum we can get is 1+2+3+4 = 10 and since 10 > 1, there are no valid combination.


Approach: We can use backtracking here. The recursive relation would be something like below:

f(k, n, i) = f(k - 1, n - i, i - j), where j is (i + 1)....9


Implementation in C#:

    public IList<IList<int>> CombinationSum3(int k, int n)
    {
        var currNums = new List<int>();
        var result = new List<IList<int>>();
        this.CombinationSum(k, n, currNums, 1, result);
        return result;
    }

    private void CombinationSum(int k,
                                int n,
                                List<int> currNums,
                                int currNum,
                                List<IList<int>> result)
    {
        if (currNums.Count == k)
        {
            if (n == 0)
            {
                Console.WriteLine($"Adding {string.Join(", ", currNums)}");
                result.Add(new List<int>(currNums));
            }
            return;
        }
        for (int i = currNum; i <= 9; ++i)
        {
            if (n - i >= 0)
            {
                currNums.Add(i);
                this.CombinationSum(k, n - i, currNums, i + 1, result);
                currNums.RemoveAt(currNums.Count - 1);
            }
            else
            {
                break;
            }
        }
    }


Complexity: O(2 ^ 9)

Sunday, June 26, 2022

[LeetCode] Word Search II

Problem: Given an m x n board of characters and a list of strings words, return all words on the board.

Each word must 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 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"]

Input: board = [["a","b"],["c","d"]], words = ["abcb"]
Output: []


Approach: The problem is similar to the problem of search a word in a matrix except here we are given an array of words. We will have the similar backtracking approach here too but we will use Trie to make it more efficient.


Implementation:

public class TrieNode

{

    public const short ALPHABET_SIZE = 26;

    public TrieNode(char value)

    {

        this.Value = value;

        this.Children = new TrieNode[ALPHABET_SIZE];

        this.End = 0;

    }

    public char Value { get; set; }

    public TrieNode[] Children { get; set; }

    public string Word { get; set; }

    public int End { get; set; }

}


public class Trie

{

    public Trie()

    {

        this.root = new TrieNode('/'); // special charater for root

    }


    public void Insert(string word)

    {

        TrieNode node = root;

        foreach(char ch in word)

        {

            if (node.Children[ch - 'a'] == null)

            {

                node.Children[ch - 'a'] = new TrieNode(ch);

            }

            node = node.Children[ch - 'a'];

        }

        node.End++;

        node.Word = word;

    }

    

    public void SearchOnBoard(char[][] board, int i, int j, List<string> result)

    {

        if (this.root.Children[board[i][j] - 'a'] == null)

        {

            return;

        }

        this.SearchOnBoard(this.root, board, i, j, result);     

    }

    

    private void SearchOnBoard(TrieNode node, char[][] board, int i, int j, List<string> result)

    {

        int index = board[i][j] - 'a';

        if (board[i][j] == '#' || node.Children[index] == null)

        {

            return;

        }

        node = node.Children[index];

        if (node.End > 0)

        {

            result.Add(node.Word);

            node.End--;

        }

        char temp = board[i][j];

        board[i][j] = '#'; // visited

        if (i > 0)

        {

            SearchOnBoard(node, board, i - 1, j, result);

        }

        if (i < board.Length - 1)

        {

            SearchOnBoard(node, board, i + 1, j, result);

        }

        if (j > 0)

        {

            SearchOnBoard(node, board, i, j - 1, result);

        }

        if (j < board[0].Length - 1)

        {

            SearchOnBoard(node, board, i, j + 1, result);

        }

        board[i][j] = temp;

    }

    private TrieNode root;

}


public class Solution 

{

    public IList<string> FindWords(char[][] board, string[] words) 

    {

        List<string> result = new List<string>();

        int rows = board?.Length ?? 0;

        if (rows == 0)

        {

            return result;

        }

        int cols = board[0].Length;

        int length = words.Length;

        if (length == 0)

        {

            return result;

        }

        Trie trie = new Trie();

        foreach(string word in words)

        {

            trie.Insert(word);

        }

        for (int i = 0; i < rows; ++i)

        {

            for (int j = 0; j < cols; ++j)

            {

                trie.SearchOnBoard(board, i, j, result);

            }

        }

        return result;

    }

}


Complexity: O( rows * cols * 3 ^ (length of the largest word in words)) 

Thursday, October 7, 2021

[LeetCode] Word Search

Problem: 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:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
Output: true

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
Output: true


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.


Approach: This problem is same as finding "microsoft" in a 2D matrix. Here we just need to find any string which is given as input. We will use the exactly same backtracking approach which we applied in the previous problem.


Implementation in C#:

    public bool Exist(char[][] board, string word) 

    {

        for (int i = 0; i < board.Length; ++i)

        {

            for (int j = 0; j < board[0].Length; ++j)

            {

                if (board[i][j] == word[0])

                {

                    if (this.Search(board, i, j, word, 0))

                    {

                        return true;

                    }

                }

            }

        }

        return false;

    }

    

    private bool Search(char[][] board, int i, int j, string word, int currIndex)

    {

        if (currIndex == word.Length)

        {

            return true;

        }

        if (!this.Check(board, i, j, word[currIndex]))

        {

            return false;

        }

        char temp = board[i][j];

        board[i][j] = '#';

        bool result = this.Search(board, i - 1, j, word, currIndex + 1) ||

            this.Search(board, i + 1, j, word, currIndex + 1) ||

            this.Search(board, i, j - 1, word, currIndex + 1) ||

            this.Search(board, i, j + 1, word, currIndex + 1);

        board[i][j] = temp;

        return result;

    }

    

    private bool Check(char[][] board, int i, int j, char ch)

    {

        if (i >= 0 && i < board.Length && j >= 0 && j < board[0].Length && board[i][j] == ch)

            return true;

        return false;

    }


Complexity: O( m * n * 3 ^ l) where m is number of rows, n is number of columns and l is the length of word.

Thursday, September 23, 2021

[LeetCode] Maximum Length of a Concatenated String with Unique Characters

Problem: Given an array of strings arr. String s is a concatenation of a sub-sequence of arr which have unique characters.

Return the maximum possible length of s.

Example:

Input: arr = ["un","iq","ue"]
Output: 4
Explanation: All possible concatenations are "","un","iq","ue","uniq" and "ique".
Maximum length is 4.
Input: arr = ["cha","r","act","ers"]
Output: 6
Explanation: Possible solutions are "chaers" and "acters".
Input: arr = ["abcdefghijklmnopqrstuvwxyz"]
Output: 26


Approach: Basically we need to apply backtracking here. We will try every valid combination and get the length of the largest valid combination here.


Implementation in C#:

    public int MaxLength(IList<string> arr) 

    {

        int maxLength = 0;

        int currLength = 0;

        int[] charsVisited = new int[26];

        MaxLengthRec(arr, 0, ref maxLength, currLength, charsVisited);

        return maxLength;

    }

    

    private void MaxLengthRec(

        IList<string> arr, 

        int currIndex, 

        ref int maxLength, 

        int currLength, 

        int[] charsVisited)

    {

        for (int i = 0; i < 26; ++i)

        {

            if (charsVisited[i] > 1)

            {

                return;

            }

        }

        maxLength = Math.Max(currLength, maxLength);                

        for (int i = currIndex; i < arr.Count; ++i)

        {

            foreach(char ch in arr[i])

            {

                ++charsVisited[ch - 'a'];

            }           

            currLength += arr[i].Length;

            MaxLengthRec(arr, i + 1, ref maxLength, currLength, charsVisited);

            currLength -= arr[i].Length;

            foreach(char ch in arr[i])

            {

                --charsVisited[ch - 'a'];

            }

        }

    }


Complexity: O(2^N)

Friday, February 19, 2021

[Google Question][LeetCode] Letter Combinations of a Phone Number

Problem: 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 digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Example:


Input: digits = "23"
Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
Input: digits = ""
Output: []
Input: digits = "2"
Output: ["a","b","c"]


Approach: Nothing to explain here. We are just going to use backtracking here. Just look at the implementation to understand it in detail.


Implementation in C#:

        public IList<string> LetterCombinations(string digits)

    {
        var mapping = this.GenerateLetterMapping();
        int length = digits?.Length ?? 0;
        if (length == 0 || (length == 1
            && !mapping.ContainsKey(digits[0])))
        {
            return new List<string>();
        }
        List<string> result = new List<string>();
        var currCombination = new StringBuilder();
        this.GetLetterCombinations(digits,
                                   0,
                                   currCombination,
                                   mapping,
                                   result);
        return result;
    }

    private void GetLetterCombinations(string digits,
                                       int currIndex,
                                       StringBuilder currCombination,
                                       Dictionary<char, List<char>> mapping,
                                       List<string> result)
    {
        if (currIndex == digits.Length)
        {
            if (currCombination.Length > 0)
            {
                result.Add(currCombination.ToString());
                return;
            }
        }
        if (mapping.ContainsKey(digits[currIndex]))
        {
            foreach(char ch in mapping[digits[currIndex]])
            {
                currCombination.Append(ch);
                this.GetLetterCombinations(digits,
                                           currIndex + 1,
                                           currCombination,
                                           mapping,
                                           result);
                --currCombination.Length;
            }
        }
    }

    private Dictionary<char, List<char>> GenerateLetterMapping()
    {
        var mapping = new Dictionary<char, List<char>>();
        mapping['2'] = new List<char> {'a', 'b', 'c'};
        mapping['3'] = new List<char> {'d', 'e', 'f'};
        mapping['4'] = new List<char> {'g', 'h', 'i'};
        mapping['5'] = new List<char> {'j', 'k', 'l'};
        mapping['6'] = new List<char> {'m', 'n', 'o'};
        mapping['7'] = new List<char> {'p', 'q', 'r', 's'};
        mapping['8'] = new List<char> {'t', 'u', 'v'};
        mapping['9'] = new List<char> {'w', 'x', 'y', 'z'};
        return mapping;
    }


Complexity: O(3^m * 4 ^n) where m is the number of digits which are mapped to 3 characters and n is the number of digits which are mapped to 4 characters. At worst case it will be O(4^n).

Friday, September 4, 2020

[AirBnb] Combination Sum

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.

Example:

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.


Approach: Use backtracking.


Implementation in C#:

        public IList<IList<int>> CombinationSum(int[] candidates, int target)

        {

            IList<IList<int>> result = new List<IList<int>>();

            if (candidates == null || candidates.Length == 0)

            {

                return result;

            }

            Array.Sort(candidates);

            List<int> currResult = new List<int>();

            this.FindCombinationSumCandidates(candidates, target, result, currResult, 0);

            return result;

        }

        private void FindCombinationSumCandidates(int[] candidates, int target, IList<IList<int>> result, List<int> currResult, int currIndex)

        {

            if (target < 0)

            {

                return;

            }

            if (target == 0)

            {

                result.Add(new List<int>(currResult));

                return;

            }

            while(currIndex < candidates.Length && target - candidates[currIndex] >= 0)

            {

                currResult.Add(candidates[currIndex]);

                this.FindCombinationSumCandidates(candidates, target - candidates[currIndex], result, currResult, currIndex);

                ++currIndex;

                currResult.RemoveAt(currResult.Count - 1);

            }

        }


Complexity: O(2 ^ k) where k is is the sum of target / candidates[i] for all i = 0 to length of candidates.