Tuesday, August 31, 2021

[Google Question][LeetCode] Making A Large Island

Problem: You are given an n x n binary matrix grid. You are allowed to change at most one 0 to be 1.

Return the size of the largest island in grid after applying this operation.

An island is a 4-directionally connected group of 1s.

Example:

Input: grid = [[1,0],[0,1]]
Output: 3
Explanation: Change one 0 to 1 and connect two 1s, then we get an island with area = 3.
Input: grid = [[1,1],[1,0]]
Output: 4
Explanation: Change the 0 to 1 and make the island bigger, only one island with area = 4.
Input: grid = [[1,1],[1,1]]
Output: 4
Explanation: Can't change any 0 to 1, only one island with area = 4.


Approach: If you see it is kind of (not exactly) connected components problem in Graph. We can solve it in 2 steps:

  1. We can first apply DFS from every cell which is not visited and has value 1. We will make this current cell parent for every reachable cell with value 1 using a map / 2D array. We also record the size of this component and we can store in a map with mapping like {parent_cell => component_size}.
  2. We again parse the grid and whenever we see a cell with 0. We can calculate it's neighbours component size and sum them. We add 1 into this sum as we are going to make this cell 1. This is what the island size we can get by changing this cell from 0 to 1. We can just take the maximum of all such islands' sizes and that will be our answer. 


Implementation in C#:

    public int LargestIsland(int[][] grid) 

    {

        int rows = grid?.Length ?? 0;

        if (rows == 0)

        {

            return 0;

        }

        int cols = grid[0].Length;

        // To store component size

        int[,] componentSizeMap = new int[rows, cols];

        // To store component's parent

        KeyValuePair<int?, int?>[,] componentParentMap = new KeyValuePair<int?, int?>[rows, cols];

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

        {

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

            {

                KeyValuePair<int?, int?> currCell = new KeyValuePair<int?, int?>(i, j);

                if (grid[i][j] == 1 && 

                    componentParentMap[i,j].Equals(default(KeyValuePair<int?, int?>)))

                {

                    int componentSize = 0;

                    this.MakeComponentUsingDFS(

                        grid, 

                        componentParentMap, 

                        currCell, 

                        i, 

                        j, 

                        ref componentSize);          

                    componentSizeMap[i, j] = componentSize;

                }

            }

        }        

        int maxSize = 0;

        bool hasZero = false;

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

        {

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

            {

                if (grid[i][j] == 0)

                {

                    hasZero = true;

                    maxSize = Math.Max(

                        this.GetNeighborComponetSizesSum(

                            grid, 

                            i, 

                            j, 

                            componentParentMap, 

                            componentSizeMap) + 1, 

                        maxSize);

                }

            }

        }

        return hasZero ? maxSize : rows * cols;

    }

    

    private int GetNeighborComponetSizesSum(

        int[][] grid, 

        int row, 

        int col, 

        KeyValuePair<int?, int?>[,] componentParentMap, 

        int[,] componentSizeMap)

    {

        // Taking hashset as multiple neighbours can have same parent.

        HashSet<KeyValuePair<int?, int?>> parentsSet = new HashSet<KeyValuePair<int?, int?>>();

        //Left Neighbor

        if (this.IsValidCell(grid, row - 1, col))

        {

            parentsSet.Add(componentParentMap[row - 1, col]);

        }

        //Right Neighbor

        if (this.IsValidCell(grid, row + 1, col))

        {

            parentsSet.Add(componentParentMap[row + 1, col]);

        }

        

        //Top Neighbor

        if (this.IsValidCell(grid, row, col - 1))

        {

            parentsSet.Add(componentParentMap[row, col - 1]);

        }

        //down Neighbor

        if (this.IsValidCell(grid, row, col + 1))

        {

            parentsSet.Add(componentParentMap[row, col + 1]);

        }

        int neighborSize = 0;

        foreach (var parent in parentsSet)

        {

            neighborSize += componentSizeMap[parent.Key.Value, parent.Value.Value];

        }

        return neighborSize;

    }

    

    private void MakeComponentUsingDFS(

        int[][] grid, 

        KeyValuePair<int?, int?>[,] componentParentMap, 

        KeyValuePair<int?, int?> parent, 

        int currRow, 

        int currCol, 

        ref int componentSize)

    {

        if (!componentParentMap[currRow, currCol].Equals(default(KeyValuePair<int?, int?>)))

        {

            return;

        }

        componentParentMap[currRow, currCol] = parent;

        ++componentSize;

        // Left Neighbor

        if (this.IsValidCell(grid, currRow -  1, currCol))

        {

            this.MakeComponentUsingDFS(

                grid, 

                componentParentMap, 

                parent, 

                currRow -  1, 

                currCol, 

                ref componentSize);

        }

        // Right Neighbor

        if (this.IsValidCell(grid, currRow +  1, currCol))

        {

            this.MakeComponentUsingDFS(

                grid, 

                componentParentMap, 

                parent, 

                currRow +  1, 

                currCol, 

                ref componentSize);

        }

        // Top Neighbor

        if (this.IsValidCell(grid, currRow, currCol - 1))

        {

            this.MakeComponentUsingDFS(

                grid, 

                componentParentMap, 

                parent, 

                currRow, 

                currCol - 1, 

                ref componentSize);

        }        

        // Down Neighbor

        if (this.IsValidCell(grid, currRow, currCol + 1))

        {

            this.MakeComponentUsingDFS(

                grid, 

                componentParentMap, 

                parent, 

                currRow, 

                currCol + 1, 

                ref componentSize);

        }

    }

    

    private bool IsValidCell(int[][] grid, int row, int col)

    {

        return row >= 0

            && row < grid.Length

            && col >= 0

            && col < grid[0].Length

            && grid[row][col] == 1;

    }


Complexity: O(n^2)

Monday, August 30, 2021

[LeetCode]Range Addition II

Problem: You are given an m x n matrix M initialized with all 0's and an array of operations ops, where ops[i] = [ai, bi] means M[x][y] should be incremented by one for all 0 <= x < ai and 0 <= y < bi.

Count and return the number of maximum integers in the matrix after performing all the operations.

Example:

Input: m = 3, n = 3, ops = [[2,2],[3,3]]
Output: 4
Explanation: The maximum integer in M is 2, and there are four of it in M. So return 4.
Input: m = 3, n = 3, ops = [[2,2],[3,3],[3,3],[3,3],[2,2],[3,3],[3,3],[3,3],[2,2],[3,3],[3,3],[3,3]]
Output: 4
Input: m = 3, n = 3, ops = []
Output: 9


Approach: We can always opt the obvious brute force that is take a matrix of m x n and then apply all the operations. Once done we can parse the matrix and get the result but again it is expensive solution like any brute force solution. Let's try to optimize it.

Given in each operation, we are always incrementing matrix cells by one and this operation is applied from cell (0, 0] to (ai, bi), we can safely say the maximum number is always will be on the (0, 0) as in every operation value at cell (0, 0) is always going to be incremented. 

Now let's think about what will be the maximum number in the matrix?  Since we are increasing the values in the cells by 1, the maximum value in the matrix is always going to be the number of operations i.e. the length of ops. Now our final answer will be the number of cells having this value. How do we get it?

We just need to find the min of all ai's i.e. min of ops[i][0] where i = 0 to length of ops and bi's i.e. min of ops[i][1] where i = 0 to length of ops. Why min? What it is telling this row(min of ai's) x col(min of bi's) sub-matrix are always incremented by all the operations since all the operations are getting applied from  (0,0) to (ai, bi) so our answer will be row * col. 


Implementation in C#:

    public int MaxCount(int m, int n, int[][] ops) 

    {

        int maxNum = ops.Length;

        if (maxNum == 0)

        {

            return m * n;

        }

        int minRow = int.MaxValue, minCol = int.MaxValue;

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

        {

            minRow = Math.Min(minRow, ops[i][0]);

            minCol = Math.Min(minCol, ops[i][1]);

        }    

        return minRow * minCol;

    }


Complexity: O(n) where n is the length of ops.

[LeetCode] Range Addition

Problem: Assume you have an array of length n initialized with all 0's and are given k update operations.

Each operation is represented as a triplet: [startIndex, endIndex, inc] which increments each element of subarray A[startIndex ... endIndex] (startIndex and endIndex inclusive) with inc.

Return the modified array after all k operations were executed.

Example:

Input: n = 5, updates = [[1,3,2], [2,4,3],[0,2,-2]
Output: [-2,0,3,5,3]
Explanation: Initial Array is [0,0,0,0,0]. Update [1,3,2] will make the array [0,2,2,2,0]. Update [2,4,3] will make array [0,2,5,5,3] and the last update [0,2,-2] will make the array [-2,0,3,5,3].

Approach: The obvious brute force approach would be to apply updates one by one but that is expensive as it can take O(n * k) time. Let's try to do better than this.

Here is the intuition of the approach:

  • There is only one read query on the entire range, and it occurs at the end of all update queries. Additionally, the order of processing update queries is irrelevant as all the updates are addition only. Therefore, we don’t have to process the entire range until the end of the updates.
  • Cumulative sums operations apply the effects of past elements to the future elements in the sequence.

What we are trying to do here, we are kind of marking the given range that we need to add this element from start to end and in the end we will just calculate the cumulative sum. Here are the steps which we do for every update [start, end, inc]:

  1. arr[start]+= inc
  2. arr[end + 1] -= inc
and in the end we need to apply the cumulative sum:
  • FOR i = 1...n-1
    • arri[i] += arr[i-1]

In this way we are kind of marking start, end  and inc. if you see when we take the cumulative sum it will update the array from start to end by adding inc and as we don't want to add inc after end index, we just added -inc to nullify it while calculating cumulative sum. 

Let's see from the above example. Initial array is [0,0,0,0,0]. Say there was only one update which is [1,3,2]. Let's apply our steps:

  • arr[1] += 2
  • arr[4] -= 2

so the array now becomes [0,2,0,0,-2]. Now once we apply cumulative sum step the array will become [0,2,2,2,0] which is what we wanted. In the same way if we apply all the updates and then apply the cumulative sum step it will give us the desired result.


Implementation in C#:

    public int[] GetModifiedArray(int length, int[][] updates) 

    {

        int[] result = new int[length];

        foreach (int[] update in updates) 

        {

            int start = update[0];

            int end = update[1];

            int inc = update[2];           

            result[start] += inc;

            if (end + 1 < length) 

            {

                result[end + 1] += -inc;

            }

        }

        for (int i = 1; i < length; i++) 

        {

            result[i] += result[i -1];

        } 

        return result;

    }


Complexity: O(k + n)

Friday, August 27, 2021

[LeetCode] Longest Uncommon Subsequence II

Problem: Given an array of strings strs return the length of the longest uncommon subsequence between them. If the longest uncommon subsequence does not exist, return -1.

An uncommon subsequence between two strings is a string that is a subsequence of one but not the other.

subsequence of a string s is a string that can be obtained after deleting any number of characters from s.

  • For example, "abc" is a subsequence of "aebdc" because you can delete the underlined characters in "aebdc" to get "abc". Other subsequences of "aebdc" include "aebdc", "aeb", and "" (empty string).

Example:

Input: strs = ["aba","cdc","eae"]
Output: 3
Input: strs = ["aaa","aaa","aa"]
Output: -1


Approach: This problem is an extension of the previous problem Longest Uncommon Subsequence I where we need to find the longest uncommon subsequence between two strings. Here instead of two strings we are given the array of strings.

Our approach remains same, we just need to find the longest substring which is not a subsequence of all the other substrings and its length will be our answer. We can sort strs using their length in descending order to make it little bit efficient. That's all!


Implementation in C#:

    public int FindLUSlength(string[] strs) 

    {        

        Array.Sort(strs, (s1, s2) => {

           return s2.Length.CompareTo(s1.Length); 

        });

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

        {

            int j = 0;

            for (; j < strs.Length; ++j)

            {

                if (i == j)

                {

                    continue;

                }                

                if (this.IsSubSequence(strs[i], strs[j]))

                {

                    break;

                }

            }  

            if (j == strs.Length)

            {

                return strs[i].Length;

            }

        }

        return -1;

    }

    

    private bool IsSubSequence(string s1, string s2)

    {   

        int s1Itr = 0;

        for (int s2Itr = 0; s1Itr < s1.Length && s2Itr < s2.Length; ++s2Itr)

        {

            if (s1[s1Itr] == s2[s2Itr])

            {

                ++s1Itr;

            }

        }        

        return s1Itr == s1.Length;

    }


Complexity: O(n^2 * len) where len is the length of longest string in  strs.

[LeetCode] Longest Uncommon Subsequence I

Problem: Given two strings a and b, return the length of the longest uncommon subsequence between a and b. If the longest uncommon subsequence does not exist, return -1.

An uncommon subsequence between two strings is a string that is a subsequence of one but not the other.

A subsequence of a string s is a string that can be obtained after deleting any number of characters from s.

  • For example, "abc" is a subsequence of "aebdc" because you can delete the underlined characters in "aebdc" to get "abc". Other subsequences of "aebdc" include "aebdc", "aeb", and "" (empty string).

Example:

Input: a = "aba", b = "cdc"
Output: 3
Explanation: One longest uncommon subsequence is "aba" because "aba" is a subsequence of "aba" but not "cdc".
Note that "cdc" is also a longest uncommon subsequence.
Input: a = "aaa", b = "bbb"
Output: 3
Explanation: The longest uncommon subsequences are "aaa" and "bbb".
Input: a = "aaa", b = "aaa"
Output: -1
Explanation: Every subsequence of string a is also a subsequence of string b. Similarly, every subsequence of string b is also a subsequence of string a.


Approach: The approach is very straight forward. See if both a and b are same then answer is obvious which is -1.

If they are not same then surely longest string will be the uncommon subsequence and its length will be our answer 

For example say a is "abcd" and b is "xyz", if you see both are uncommon sequences but since "abcd" is the longest one and we need to return its length.

Say a = "aaa" and b = "aa" then if you see b is a subsequence a but a is not subsequence of b so a is the longest uncommon sequence and we need to return it's length.


Implementation in C#:

    public int FindLUSlength(string a, string b) 

    {

        if (a == b)

        {

            return -1;

        }    

        return a.Length < b.Length ? b.Length : a.Length;

    }


Complexity: O(n) where n is the length of longest string.

Wednesday, August 25, 2021

[LeetCode] Sum of Square Numbers

Problem: Given a non-negative integer c, decide whether there're two integers a and b such that a^2 + b^2 = c.

Example:

Input: c = 5
Output: true
Explanation: 1 * 1 + 2 * 2 = 5
Input: c = 3
Output: false
Input: c = 4
Output: true
Input: c = 2
Output: true
Input: c = 1
Output: true


Approach: If you look at the problem closely, this is similar to the the problem of finding two elements in a sorted array with unique integers whose sum is equal to a given value

The sorted array here is the squares of 0,1,2,.....(int)sqrt(c) and the given value is c. We can use the same two pointer approach (one from the start and one from the end) to solve this problem.


Implementation in C#:

    public bool JudgeSquareSum(int c) 

    {

        int start = 0;

        int end = (int)Math.Sqrt(c);        

        while (start <= end)

        {

            int sqrSum = start * start + end * end;

            if (sqrSum == c)

            {

                return true;

            }

            else if (sqrSum < c)

            {

                ++start;

            }

            else

            {

                --end;

            }

        }        

        return false;

    }


Complexity: O(sqrt(c)

Tuesday, August 24, 2021

[LeetCode] Complex Number Multiplication

Problem: A complex number can be represented as a string on the form "real+imaginaryi" where:

  • real is the real part and is an integer in the range [-100, 100].
  • imaginary is the imaginary part and is an integer in the range [-100, 100].
  • i^2 == -1.

Given two complex numbers num1 and num2 as strings, return a string of the complex number that represents their multiplications.

Example:

Input: num1 = "1+1i", num2 = "1+1i"
Output: "0+2i"
Explanation: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i, and you need convert it to the form of 0+2i.
Input: num1 = "1+-1i", num2 = "1+-1i"
Output: "0+-2i"
Explanation: (1 - i) * (1 - i) = 1 + i2 - 2 * i = -2i, and you need convert it to the form of 0+-2i.


Approach: Let's say num1 is (r1 + i1i) and num2 is (r2 + i2i). Lets see how its multiplication looks like:

(r1 + i1i) * (r2 + i2i) = r1*r2 + r1*i2*i + r2*i1*i + i1*i2*i^2 

Given i^2 = -1, Now our multiplication become- 

(r1* r2 - i1 * i2) + (r1 * i2 + r2 * i1)i

Now we just need to split the num1 and num2 string on '+' and 'i' and get the real and imaginary parts of the complex number and then we just need to apply the above formula. That's it!


Implementation in C#:

    public string ComplexNumberMultiply(string num1, string num2) 

    {

        string[] nums1 = num1.Split('+', 'i');

        string[] nums2 = num2.Split('+', 'i');

        int r1 = int.Parse(nums1[0]);

        int i1 = int.Parse(nums1[1]);

        int r2 = int.Parse(nums2[0]);

        int i2 = int.Parse(nums2[1]);        

        return $"{r1 * r2 - i1 * i2}+{r1 * i2 + r2 * i1}i";

    }


Complexity: O(1)

Monday, August 23, 2021

[Google Question] Given a binary tree with string stored leaf node and number of characters in left subtree stored in other nodes, get the substring given index and length.

Problem: You are given a binary tree in which every leaf node has a string and other nodes have the number of characters in their left subtree.  

Given an index and length, return the substring from the binary tree. 

Example:

Chart

Description automatically generated

Input: Tree: above tree, index = 2, length = 10
Output: As pointed in the image "cdefghijkl"
Explanation: If you see the string is "abcdefghilmno", Its substring (2, 10) is "cdefghijkl".


Approach: We can use preorder traversal here. We need to first find the character at given index. Once we find that character, after that we don't need index (we can make it 0) as we just need "length" number of characters.  

Look at the implementation to understand the approach in detail.


Implementation in C#:

      public static string GetString(BinaryTreeStringNode root, int index, int len) 

        { 

            if (root == null) 

            { 

                return string.Empty; 

            } 

            return GetStringInternal(root, ref index, len); 

        } 


        private static string GetStringInternal(BinaryTreeStringNode node, ref int index, int len) 

        { 

            if (node == null) 

            { 

                return string.Empty; 

            } 

            // Got the leaf node, get the substring 

            if (node.Left == null && node.Right == null) 

            { 

                string result = node.Value.Substring(index, len); 

                // Get the starting point, now make index 0 as we just need 'len' number of chars 

                index = 0; 

                return result; 

            } 

            int leftCharsLength = int.Parse(node.Value); 

            // No need to go to left subtree 

            if (leftCharsLength <= index) 

            { 

                index = index - leftCharsLength; 

                return GetStringInternal(node.Right, ref index, len); 

            } 

            // No need to go to right subtree 

            if (leftCharsLength >= index + len) 

            { 

                return GetStringInternal(node.Left, ref index, len); 

            } 

            int numCharsFoundInLeftTree = leftCharsLength - index; 

            return GetStringInternal(node.Left, ref index, numCharsFoundInLeftTree) + GetStringInternal(node.Right, ref index, len - numCharsFoundInLeftTree); 

        }


Complexity: O(n) assuming string operation will take constant time.

[LeetCode] Two Sum IV - Input is a BST

Problem: Given the root of a Binary Search Tree and a target number k, return true if there exist two elements in the BST such that their sum is equal to the given target.

Example:

Input: root = [5,3,6,2,4,null,7], k = 9
Output: true
Input: root = [5,3,6,2,4,null,7], k = 28
Output: false
Input: root = [2,1,3], k = 4
Output: true
Input: root = [2,1,3], k = 1
Output: false
Input: root = [2,1,3], k = 3
Output: true


Approach: There are multiple approaches we can take here to solve this problem. One of simplest approach would be to take one node of binary search tree and then search for k - node.val in the input binary search tree. Here is how the approach looks like:

  • DEF DoesSumExist(root, node, k):
    • IF node == NULL
      • RETURN FALSE
    • IF (DoesElementExist(root , k - node.val))
      • RETURN TRUE
    • RETURN DoesSumExist(root, node.left, k) OR DoesSumExist(root, node.right, k)

  • DEF DoesElementExist(node, val):
    • WHILE node ! = null
      • IF node.val == val
        • RETURN TRUE
      • ELSE IF node.val < val
        • node = node.right
      • ELSE
        • node = node.left
    • RETURN FALSE

The above approach will work but this approach will take O(nlogn) time. Let's try to optimize it. What we can do is we can store this BSTree in an array using inorder traversal. This will make sure the array is sorted. Now we can use two pointer approach to find the two elements whose sum is equal to k. Here is the algorithm:

  • DEF DoesSumExist(root, k)
    • sortedArray = []
    • PutElementsInSortedArray(root, sortedArray)
    • RETURN DoesSumExistInSortedArray(sortedArray, k)

  • DEF PutElementsInSortedArray(node, sortedArray)
    • IF node != null
      • PutElementsInSortedArray(node.left, sortedArray)
      • sortedArray.Add(node.val)
      • PutElementsInSortedArray(node.right, sortedArray)

  • DEF DoesSumExistInSortedArray(sortedArray, k)
    • start = 0, end = Length(sortedArray) - 1
    • WHILE start < end
      • sum = sortedArray[start] + sortedArray[end]
      • IF sum == k
        • RETURN TRUE
      • ELSE sum < k
        • start = start + 1
      • ELSE
        • end = end - 1
    • RETURN FALSE

The above approach will solve the problem in linear time which is definitely a considerable improvement but if you see we are traversing the same elements twice (once in tree and once in array) and also we are taking O(n) space every time. Let's try to do better!

We can use hashing here to solve this problem efficiently. We can use any traversal and we keep putting the values(node.val) in the hash set. Now at any time if we find k - node.val in hash set we can return true immediately otherwise we keep on traversing the tree. Here is the algorithm of this approach:

  • DEF DoesSumExist(root, k):
    • hashSet = []
    • RETURN DoesSumExistUsingSet(root, k, hashSet)

  • DEF DoesSumExistUsingSet(node, k, hashSet):
    • IF node == null
      • RETURN FALSE
    • IF hashSet has (k - node.val)
      • RETURN TRUE
    • hashSet.Add(node.val)
    • RETURN DoesSumExistUsingSet(node.left, k hashSet) OR DoesSumExistUsingSet(node.right, k, hashSet)

You can see we are just traversing tree once in this approach and also the extra space could be less than number of elements in the tree. However HashSet could take more space than an array or list.

I am giving the implementation of Approach 2 and Approach 3.


Implementation in C#:

Approach 2: Flatten the BSTree to Sorted Array:

    public bool FindTarget(TreeNode root, int k) 

    {

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

        this.FlattenBSTree(root, flattenBSTreeList);

        return this.IsSumExist(flattenBSTreeList, k);

    }

    

    private void FlattenBSTree(TreeNode node, List<int> flattenBSTreeList)

    {

        if (node != null)

        {

            this.FlattenBSTree(node.left, flattenBSTreeList);

            flattenBSTreeList.Add(node.val);

            this.FlattenBSTree(node.right, flattenBSTreeList);

        }

    }


    private bool IsSumExist(List<int> flattenBSTreeList, int k)

    {

        int start = 0, end = flattenBSTreeList.Count - 1;

        while (start < end)

        {

            int currSum = flattenBSTreeList[start] + flattenBSTreeList[end];

            if (currSum == k)

            {

                return true;

            }

            else if (currSum < k)

            {

                ++start;

            }

            else

            {

                --end;

            }

        }

        return false;

    }


Approach 3: Using Hashing:

    public bool FindTarget(TreeNode root, int k) 

    {    

        HashSet<int> nodeValSet = new HashSet<int>();

        return this.IsSumExist(root, nodeValSet, k);

    }

    

    private bool IsSumExist(TreeNode node, HashSet<int> nodeValSet, int k)

    {

        if (node == null)

        {

            return false;

        }        

        if (nodeValSet.Contains(k - node.val))

        {

            return true;

        }

        nodeValSet.Add(node.val);        

        return this.IsSumExist(node.left, nodeValSet, k) || 

            this.IsSumExist(node.right, nodeValSet, k);

    }


Complexity: O(n) but preferred is approach 3 as it does only one traversal.