Tuesday, March 23, 2021

[Google Question][LeetCode] Fruit Into Baskets

Problem: In a row of trees, the i-th tree produces fruit with type tree[i]. You start at any tree of your choice, then repeatedly perform the following steps:

  1. Add one piece of fruit from this tree to your baskets.  If you cannot, stop.
  2. Move to the next tree to the right of the current tree.  If there is no tree to the right, stop.

Note that you do not have any choice after the initial choice of starting tree: you must perform step 1, then step 2, then back to step 1, then step 2, and so on until you stop.

You have two baskets, and each basket can carry any quantity of fruit, but you want each basket to only carry one type of fruit each.

What is the total amount of fruit you can collect with this procedure?

Example:

Input: [1,2,1]
Output: 3
Explanation: We can collect [1,2,1].
Input: [0,1,2,2]
Output: 3
Explanation: We can collect [1,2,2]. If we started at the first tree, we would only collect [0, 1].
Input: [1,2,3,2,2]
Output: 4
Explanation: We can collect [2,3,2,2]. If we started at the first tree, we would only collect [1, 2].
Input: [3,3,3,1,2,1,1,2,3,3,4]
Output: 5
Explanation: We can collect [1,2,1,1,2]. If we started at the first tree or the eighth tree, we would only collect 4 fruits.


Approach: Basically we can use sliding window here. We increment the right boundary till we have only 2 types. Once we see we get more than 2 types in the window, we keep incrementing the left and also we keep reduce the current count till we get only 2 types in the current window.

In the end we just need to return the max of all the current counts. That's all!


Implementation  in C#:

        public int TotalFruit(int[] tree)

        {

            Dictionary<int, int> freqMap = new Dictionary<int, int>();

            int maxCount = 0;

            int left = 0, right = 0;

            while (right < tree.Length)

            {

                if (freqMap.ContainsKey(tree[right]))

                {

                    ++freqMap[tree[right]];

                }

                else

                {

                    if (freqMap.Count == 2)

                    {

                        int i = right - 2;

                        // No need to remove continuous elements which are same as element at previous index

                        while (i >= left && tree[i] == tree[i + 1])

                        {

                            --i;

                        }

                        int tempLeft = i + 1;

                        while (i >= left)

                        {

                            --freqMap[tree[i]];

                            if (freqMap[tree[i]] == 0)

                            {

                                freqMap.Remove(tree[i]);

                            }

                            --i;

                        }

                        left = tempLeft;

                    }

                    freqMap[tree[right]] = 1;

                }

                maxCount = Math.Max(maxCount, right - left + 1);

                ++right;

            }

            return maxCount;

        }


Complexity: O(n)

[Google Question][LeetCode] License Key Formatting

Problem: You are given a license key represented as a string S which consists only alphanumeric character and dashes. The string is separated into N+1 groups by N dashes.

Given a number K, we would want to reformat the strings such that each group contains exactly K characters, except for the first group which could be shorter than K, but still must contain at least one character. Furthermore, there must be a dash inserted between two groups and all lowercase letters should be converted to uppercase.

Given a non-empty string S and a number K, format the string according to the rules described above.

Example:

Input: S = "5F3Z-2e-9-w", K = 4

Output: "5F3Z-2E9W"

Explanation: The string S has been split into two parts, each part has 4 characters.
Note that the two extra dashes are not needed and can be removed.
Input: S = "2-5g-3-J", K = 2

Output: "2-5G-3J"

Explanation: The string S has been split into three parts, each part has 2 characters except the first part as it could be shorter as mentioned above.


Approach: The approach is straight forward. You can understand it by just looking at the code.


Implementation in C#:

    public string LicenseKeyFormatting(string S, int K) 

    {

        S = S.Replace("-", "");

        if (string.IsNullOrWhiteSpace(S))

        {

            return "";

        }

        int length = S.Length;

        int charsinFirstGroup = length % K == 0 ? K : length % K;

        StringBuilder builder = new StringBuilder();

        int j = 0;

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

        {

            builder.Append(char.ToUpper(S[j++]));

        }

        builder.Append('-');

        while (j < length)

        {

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

            {

                builder.Append(char.ToUpper(S[j++]));

            }

            builder.Append('-');

        }

        builder.Length--;

        return builder.ToString();

    }


Complexity: O(n)

[Google Question] Unique Email Addresses

Problem: Every valid email consists of a local name and a domain name, separated by the '@' sign. Besides lowercase letters, the email may contain one or more '.' or '+'.

For example, in "abc@xyz.com", "abc" is the local name, and "xyz.com" is the domain name.

If you add periods '.' between some characters in the local name part of an email address, mail sent there will be forwarded to the same address without dots in the local name. Note that this rule does not apply to domain names.

For example, "abc.d@xyz.com" and "abcd@xyz.com" forward to the same email address.

If you add a plus '+' in the local name, everything after the first plus sign will be ignored. This allows certain emails to be filtered. Note that this rule does not apply to domain names.

For example, "ab.c+lmn@xyz.com" will be forwarded to "abc@xyz.com".

It is possible to use both of these rules at the same time.

Given an array of strings emails where we send one email to each email[i], return the number of different addresses that actually receive mails.

Example:

Input: emails = ["abc.d+lmn@xyz.com","ab.c.d+lmn.pqr@xyz.com","abcd+pqr@xy.z.com"]
Output: 2
Explanation: "abcd@lxyz.com" and "abcd@xy.z.com" actually receive mails.


Approach: We can remove all the characters from local name which comes after the '+' and also we can replace all '.' with empty string on all the input strings.

We can add the result of the above operations in a HashSet and in the end we can just return the count of hashset.


Implementation in C#:

    public int NumUniqueEmails(string[] emails) 

    {

        int length = emails?.Length ?? 0;

        HashSet<string> uniqueEmailAddressSet = new HashSet<string>();

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

        {

            string[] names = emails[i].Split('@');

            string localName = names[0];

            string domainName = names[1];

            int index = localName.IndexOf('+');

            if (index != -1)

            {

                localName = localName.Substring(0, index);

            }

            localName = localName.Replace(".", "");

            uniqueEmailAddressSet.Add(localName + "@" + domainName);

        }

        return uniqueEmailAddressSet.Count;

    }


Complexity: O(n) where is n is the number of characters in the input string array

[LeetCode] Find Smallest Common Element in All Rows

Problem: Given a matrix mat where every row is sorted in strictly increasing order, return the smallest common element in all rows.

If there is no common element, return -1.

Example:

Input: mat = [[1,2,3,4,5],[2,4,5,8,10],[3,5,7,9,11],[1,3,5,7,9]]
Output: 5


Approach: The thing to catch here is every row is sorted in strictly increasing order that means there will be no duplicates in a row. We can use this fact.

If we count the occurrences of every element in the matrix and whenever we find the count of element is equal to the number of rows that means this element is common to all rows. Now we just need to find out the minimum of such elements.

We can calculate frequency using a hash table.


Implementation in C#:

    public int SmallestCommonElement(int[][] mat) 

    {

        Dictionary<int, int> freqMap = new Dictionary<int, int>();

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

        {

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

            {

                if (!freqMap.ContainsKey(mat[i][j]))

                {

                    freqMap[mat[i][j]] = 0;

                }

                

                ++freqMap[mat[i][j]];

            }

        }

        int min = int.MaxValue;

        foreach(var entry in freqMap)

        {

            if (entry.Value == mat.Length)

            {

                min = min > entry.Key ? entry.Key : min;

            }

        }

        return min == int.MaxValue ? -1 : min;

    }


Complexity: O(m * n) m is number of rows and n is number of columns

Friday, March 19, 2021

[Google] Count number of node in Complete Binary Tree

Problem: Given the root of a complete binary tree, return the number of the nodes in the tree. A complete binary tree is completely filled except the last level. All nodes in the last level are as far left as possible.

Approach: We could have a linear time approach and count the nodes using any traversal like following:

  • CountNode(TreeNode node)
    • IF node ==  null
      • RETURN 0
    • RETURN 1 + CALL CountNode(node.Left) + CALL CountNode(node.Right)

But here we are not taking the advantage of given binary tree being a complete binary tree. We know that all the levels except last level will be completely filled. That means if the depth of the tree is d then we know that we will get 2 ^ d - 1 nodes for sure. Now we just need to add the number of nodes in the last level to this and it will be our answer.

Now the question is how to get the number of nodes at the last level. We know that all the nodes at this level will be as far left as possible. That means we can apply the binary search here which we used to find the element in level order sorted complete binary tree using the gray code.

That's all!


Implementation in C#:

    public int CountNodes() 

    {

        if (this.Root == null)

        {

            return 0;

        }

        int depth = this.GetDepth();

        if (depth == 0)

        {

            return 1;

        }

        int leftNodesAtLastLevel = this.CountNodesAtLastLevelBinarySearch(depth, 0, (int)Math.Pow(2, depth) - 1);

        return (int)Math.Pow(2, depth) - 1 + leftNodesAtLastLevel;

    }

    

    private int CountNodesAtLastLevelBinarySearch(int level, int left, int right)

    {

        while (left <= right)

        {

            int mid = (left + right) / 2;

            if (this.GetNodeAtIndex(mid, level) != null)

            {

                left = mid + 1;

            }

            else

            {

                right = mid - 1;

            }

        }

        return left;

    }

    

    private BinaryTreeNode GetNodeAtIndex(int index, int level)

    {

        int[] grayCode = this.GetGrayCode(index, level);

        BinaryTreeNode node = this.Root;

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

        {

            if (grayCode[i] == 0)

            {

                node = node.LeftNode;

            }

            else

            {

                node = node.RightNode;

            }

        }

        return node;

    }

    

    private int[] GetGrayCode(int index, int numOfBits)

    {

        int[] grayCode = new int[numOfBits];

        int i = numOfBits - 1;

        while (index > 0)

        {

            grayCode[i--] = index % 2;

            index /= 2;

        }

        return grayCode;

    }        

    

    private int GetDepth()

    {

        BinaryTreeNode node = this.Root;

        int depth = 0;

        while (node.LeftNode != null)

        {

            node = node.LeftNode;

            ++depth;

        }

        return depth;

    }


Complexity: O((logn)^2) where n is number of nodes so logn is depth of the tree.

Thursday, March 18, 2021

[Google Question][LeetCode] Trapping Rain Water II (2D elevation )

Problem: Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining.

Example:

Given the following 3x6 height map:
[
  [1,4,3,1,3,2],
  [3,2,1,3,2,4],
  [2,3,3,2,3,1]
]

Return 4.


The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] before the rain.

 

After the rain, water is trapped between the blocks. The total volume of water trapped is 4.


Approach: To understand the approach, let's take an example:

[

  [12,13,1,12],

  [13,4,13,12],

  [13,8,10,12],

  [12,13,12,12],

  [13,13,13,13]

]

1. Let's take a cell (1,1) with height = 4. It's neighbors heights are 13, 13, 8, 13 (top, right, bottom, left). The minimum height among all the neighbors are 8 so if it was just these cells the cell (1, 1) could hold (8 - 4 =) 4 units of water but now if you see the neighbor cell with minimum height(8) is (2, 1). This cell is surrounded by the cells with heights 4, 10, 13, 13. We don't care about 4 as it is less than 8 so the minimum height among all other cells is 10 so if you visualize this matrix as 2D elevation map, you will realize that cell (2, 1) can hold 10 - 8 = 2 units of water which is obvious but the amount of water at (1, 1) will also change and it will become 10 - 4 = 6 units of water.

Now we again have to look at what is the minimum length of neighbors of cell with height 10 which is (2, 2). The heights are 13, 12, 12, 8. The minimum height is 12 (8 is not considered as it is less than 10) so we can say cell (2, 2) can hold 12 - 10 = 2 units of water but if you see the amount of water at cells (2, 1), (1, 1) will also be changed and it will become 12 - 8 = 4 units and 12 - 4 =8 units respectively. 

2. All the boundaries cell can't hold water as they are open from at least one side.

If you read point 1, you will see that we are picking the next minimum cell and calculating the amount of water so we can use Priority Queue here with height as comparer.

Basically we do kind of BFS where we use priority queue instead of a normal queue.

That's all!


Implementation in C#:

    public class HeightComparer : IComparer<int[]>

    {

        public int Compare([AllowNull] int[] x, [AllowNull] int[] y)

        {

            return x[2].CompareTo(y[2]);

        }

    }


    public int TrapRainWater(int[][] heightMap)

        {

            int rows = heightMap?.Length ?? 0;

            if (rows <= 1)

            {

                return 0;

            }

            int cols = heightMap[0].Length;

            if (cols <= 1)

            {

                return 0;

            }

            bool[,] visited = new bool[rows, cols];

            PriorityQueue<int[]> priorityQueue = new PriorityQueue<int[]>(rows * cols, new HeightComparer());

            // Add all the boundary cells, no need to update amount of water 

            // as boundary cells are open

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

            {

                priorityQueue.Push(new int[] { i, 0, heightMap[i][0] });

                priorityQueue.Push(new int[] { i, cols - 1, heightMap[i][cols - 1] });

                visited[i, 0] = true;

                visited[i, cols - 1] = true;

            }

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

            {

                priorityQueue.Push(new int[] { 0, i, heightMap[0][i] });

                priorityQueue.Push(new int[] { rows - 1, i, heightMap[rows - 1][i] });

                visited[0, i] = true;

                visited[rows - 1, i] = true;

            }

            int currMax = 0, amountOfWater = 0;

            while (priorityQueue.Count > 0)

            {

                int[] currCell = priorityQueue.Top;

                priorityQueue.Pop();

                currMax = Math.Max(currMax, currCell[2]);

                this.AddNeighborsToPQAndUpdateWater(heightMap, priorityQueue, currCell[0], currCell[1], currMax, visited, ref amountOfWater);

            }

            return amountOfWater;

        }


        private void AddNeighborsToPQAndUpdateWater(

            int[][] heightMap, 

            PriorityQueue<int[]> priorityQueue, 

            int row, 

            int col, 

            int currMax, 

            bool[,] visited,

            ref int amountOfWater)

        {

            // Up neighbor

            if (this.IsValidAndUnvisitedCell(heightMap, row - 1, col, visited))

            {

                if (currMax > heightMap[row - 1][col])

                {

                    amountOfWater += currMax - heightMap[row - 1][col];

                }

                priorityQueue.Push(new int[] { row - 1, col, heightMap[row - 1][col] });

                visited[row - 1, col] = true;

            }

            // Down neighbor

            if (this.IsValidAndUnvisitedCell(heightMap, row + 1, col, visited))

            {

                if (currMax > heightMap[row + 1][col])

                {

                    amountOfWater += currMax - heightMap[row + 1][col];

                }

                priorityQueue.Push(new int[] { row + 1, col, heightMap[row + 1][col] });

                visited[row + 1, col] = true;

            }

            // Left Neighbor

            if (this.IsValidAndUnvisitedCell(heightMap, row, col - 1, visited))

            {

                if (currMax > heightMap[row][col - 1])

                {

                    amountOfWater += currMax - heightMap[row][col - 1];

                }

                priorityQueue.Push(new int[] { row, col - 1, heightMap[row][col - 1] });

                visited[row, col - 1] = true;

            }

            // Right neighbor

            if (this.IsValidAndUnvisitedCell(heightMap, row, col + 1, visited))

            {

                if (currMax > heightMap[row][col + 1])

                {

                    amountOfWater += currMax - heightMap[row][col + 1];

                }

                priorityQueue.Push(new int[] { row, col + 1, heightMap[row][col + 1] });

                visited[row, col + 1] = true;

            }

        }


        private bool IsValidAndUnvisitedCell(int[][] heightMap, int row, int col, bool[,] visited)

        {

            if (row < 0 || row >= heightMap.Length || col < 0 || col >= heightMap[0].Length || visited[row, col])

            {

                return false;

            }

            return true;

        }


Complexity: O(m*n*log(m*n)) where m is number of rows and n is number of cols


Wednesday, March 17, 2021

[Amazon Question] Best Time to Buy and Sell Stock with Transaction Fee

Problem: You are given an array prices where prices[i] is the price of a given stock on the ith day, and an integer fee representing a transaction fee.

Find the maximum profit you can achieve. You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction.

Note that you can't engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).

Example:

Input: prices = [1,3,7,5,10,3], fee = 3
Output: 6 Explanation: The maximum profit can be achieved by: - Buying at prices[0] = 1 - Selling at prices[4] = 10 The total profit is (10 - 1 - 3) = 6.


Approach: Like any other Buy and sell stock problem before, we are going to use DP here too. We are going to maintain two variables:

  1. maxProfitByHoldingStock: The maximum profit we can have if we hold a stock.
  2. maxProfitWithNoStock: The maximum profit we can have if we don't have any stock.

At 0th day, the values of these variables will be:

  • maxProfitByHoldingStock = -prices[0] // Buy a stock
  • maxProfitWithNoStock = 0 // Can't sell if didn't buy anything.

Now at any ith day, we can either sell a stock or buy a stock:

  • Sell a stock: maxProfitWithNoStock  = MAX(maxProfitWithNoStock, maxProfitByHoldingStock + prices[i] - fee)
  • Buy a stock: maxProfitByHoldingStock = MAX(maxProfitByHoldingStock, maxProfitWithNoStock - prices[i])

In the end, it is obvious that maxProfitWithNoStock  will be our answer.


Implementation in C#:

    public int MaxProfit(int[] prices, int fee)
    {
        int length = prices?.Length ?? 0;
        if (length == 0)
        {
            return 0;
        }
        int maxProfitWithNoStocks = 0;
        int maxProfitWithStocks = -prices[0];
        for (int i = 1; i < length; ++i)
        {
            maxProfitWithNoStocks = Math.Max(maxProfitWithNoStocks,
                                             maxProfitWithStocks + prices[i] - fee);
            maxProfitWithStocks = Math.Max(maxProfitWithStocks,
                                           maxProfitWithNoStocks - prices[i]);
        }
        return maxProfitWithNoStocks;
    }


Complexity: O(n)

[Facebook Question][LeetCode] Construct Binary Tree from String

Problem: You need to construct a binary tree from a string consisting of parenthesis and integers.

The whole input represents a binary tree. It contains an integer followed by zero, one or two pairs of parenthesis. The integer represents the root's value and a pair of parenthesis contains a child binary tree with the same structure.

You always start to construct the left child node of the parent first if it exists.

Example:

Input: s = "4(2(3)(1))(6(5))"
Output: [4,2,6,3,1,5]
Input: s = "4(2(3)(1))(6(5)(7))"
Output: [4,2,6,3,1,5,7]
Input: s = "-4(2(3)(1))(6(5)(7))"
Output: [-4,2,6,3,1,5,7]


Approach: Just by looking at the examples, we can tell the input strings are kind of preorder traversal result so we are going to use preorder traversal only. 

The only thing which we need to do is we need to get the left tree ending index in advance. Here is how our algorithm will look like:

  • StrToTree(s, start, end)
    • IF (start > end)
      • RETURN null
    • leftparanthesisStartIndex = Index of '(' in s[start...end]
    • IF no '(' in s[start...end]
      • RETURN new BinaryTreeNode(int(s[start...end]) // no subtrees just return the node
    • node = new BinaryTreeNode(int(s[start...leftparanthesisStartIndex - 1])
    • leftparanthesisEndIndex = Index of ')' corresponding to '(' at leftparanthesisStartIndex in s[leftparanthesisStartIndex...end]
    • // Recursive calls
    • node.Left = CALL StrToTree(s, leftparanthesisStartIndex + 1, leftparanthesisEndIndex - 1) // No need to include '(' and ')'
    • node.Right = CALL StrToTree( leftparanthesisEndIndex + 2, end - 1) // +2 is to skip ')' and '('
    • RETURN node 

That's all!


Implementation in C#:

    public BinaryTreeNode Str2tree(string s) 

    {

        return this.Str2tree(s, 0, s.Length - 1);

    }

    

    private BinaryTreeNode Str2tree(string s, int startIndex, int endIndex)

    {

        if (startIndex > endIndex)

        {

            return null;

        }

        int leftTreeStartIndex = this.GetLeftTreeStartIndex(s, startIndex, endIndex);

        if (leftTreeStartIndex == -1)

        {

            return new BinaryTreeNode(int.Parse(s.Substring(startIndex, endIndex - startIndex + 1)));

        }

        BinaryTreeNode node = new BinaryTreeNode(int.Parse(s.Substring(startIndex, leftTreeStartIndex - startIndex)));

        int leftTreeEndIndex = this.GetLeftTreeEndIndex(s, leftTreeStartIndex, endIndex);

        // leftTreeEndIndex == -1: Should not happen

        if (leftTreeEndIndex != -1)

        {

            node.LeftNode = this.Str2tree(s, leftTreeStartIndex + 1, leftTreeEndIndex - 1);

            node.RightNode = this.Str2tree(s, leftTreeEndIndex + 2, endIndex - 1);

        }

        return node;

    }

    

    private int GetLeftTreeEndIndex(string s, int leftTreeStartIndex, int endIndex)

    {

        int leftPranthesisCount = 0;

        for (int i = leftTreeStartIndex; i <= endIndex; ++i)

        {

            if (s[i] == '(')

            {

                ++leftPranthesisCount;

            }

            else if (s[i] == ')')

            {

                --leftPranthesisCount;

            }

            // Get the corresponding ')'

            if (leftPranthesisCount == 0)

            {

                return i;

            }

        }

        return -1;

    }

    

    private int GetLeftTreeStartIndex(string s, int startIndex, int endIndex)

    {

        for (int i = startIndex; i < endIndex; ++i)

        {

            if (s[i] == '(')

            {

                return i;

            }

        }

        return -1;

    }


Complexity: O(n)

[LeetCode] Generate Random Point in a Circle

Problem: Given the radius and x-y positions of the center of a circle, write a function randPoint which generates a uniform random point in the circle.

Note:

  1. input and output values are in floating-point.
  2. radius and x-y position of the center of the circle is passed into the class constructor.
  3. a point on the circumference of the circle is considered to be in the circle.
  4. randPoint returns a size 2 array containing x-position and y-position of the random point, in that order.

Example:

["Solution","randPoint","randPoint","randPoint"]
[[1,0,0],[],[],[]]
Output: [null,[-0.72939,-0.65505],[-0.78502,-0.28626],[-0.83119,-0.19803]]
Input: 
["Solution","randPoint","randPoint","randPoint"]
[[10,5,-7.5],[],[],[]]
Output: [null,[11.52438,-8.33273],[2.46992,-16.21705],[11.13430,-12.42337]]

Explanation of Input Syntax:

The input is two lists: the subroutines called and their arguments. Solution's constructor has three arguments, the radius, x-position of the center, and y-position of the center of the circle. randPoint has no arguments. Arguments are always wrapped with a list, even if there aren't any.


Approach: The intuition here is it's easy to generate a random point in a square than in a circle. Now we can basically generate a random point in a square of side with size of side as "2R". It will look like below image:


Now we need to check for each point if the distance between generated point and center point is <= radius. If yes, we got our point otherwise we need to generate the random point again and do the same comparison.

You may be worried that it can take a lot of time, actually infinite time. In theory it could happen but practically its not. Let's see why:

Area of circle = 3.14 * R ^ 2

Area of square = (2 * R) ^  2 = 4 * R ^ 2

That means we will get a valid point:

((3.14 * R ^ 2) / (4 *  R ^ 2)) * 100 = (3.14 / 4) * 100 = 0.785 * 100 = 78.5% of times.

That means we will run the loop approximately (1/.785) = 1.2738 times to get the valid point. 


Implementation in C#:

public class Solution 

{

    public Solution(double radius, double x_center, double y_center) 

    {

        this.radius = radius;

        this.centerX = x_center;

        this.centerY = y_center;

    }

    

    public double[] RandPoint() 

    {

        Random random = new Random();

        while (true)

        {

            double newX = (this.centerX - this.radius) + random.NextDouble() * 2 * this.radius;

            double newY = (this.centerY - this.radius) + random.NextDouble() * 2 * this.radius;

            if (Math.Sqrt(Math.Pow((newX - this.centerX), 2) + Math.Pow((newY - this.centerY), 2)) <= this.radius)

            {

                return new double[] { newX, newY };

            }

        }

    }

    double radius, centerX, centerY;

}


Complexity: O(1) on average but O(infinite) on worst case.

Monday, March 15, 2021

[Microsoft Question][LeetCode] Encode and Decode TinyURL

Problem: TinyURL is a URL shortening service where you enter a URL such as https://leetcode.com/problems/design-tinyurl and it returns a short URL such as http://tinyurl.com/4e9iAk.

Design the encode and decode methods for the TinyURL service. There is no restriction on how your encode/decode algorithm should work. You just need to ensure that a URL can be encoded to a tiny URL and the tiny URL can be decoded to the original URL.


Approach: There are many approaches which we can take:

  1. Counter: The problem here is the number of URLs can't be more than int / long range and the codes are predictable.
  2. Random number: Problem here is again the number of URLs can't be more than int / long range and also collision can happen so encryption performance can be degraded.

Now our final approach is to generate random fixed length (say length = 6) and assign it to url while encrypting. The number of URLs can be generated using this approach is 62 ^ 6 which is very large number. We can increase the length if we want more URLs. 

Prediction of next code and collision are highly unlikely. 


Implementation in C#:

public class Codec 

{

    private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private const int tinyUrlSize = 6;

    private const string tinyUrlPrefix = "http://tinyurl.com/";

    

    public string encode(string longUrl) 

    {    

        Random random = new Random();

        while(true)

        {

            string tinyUrl = string.Empty;

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

            {

                tinyUrl += chars[random.Next(chars.Length)];

            }

            if (!urlMap.ContainsKey(tinyUrl))

            {

                urlMap[tinyUrl] = longUrl;

                return tinyUrlPrefix + tinyUrl;

            }

        }

    }


    public string decode(string shortUrl) 

    {    

        string key = shortUrl.Substring(tinyUrlPrefix.Length);

        if (!urlMap.ContainsKey(key))

        {

            return string.Empty;

        }

        return urlMap[key];

    }

    Dictionary<string, string> urlMap = new Dictionary<string, string>();

}


Complexity: O(length of chars)

Friday, March 12, 2021

[Google Question][LeetCode] Course Schedule II

Problem: There are a total of n courses you have to take labelled from 0 to n - 1. Some courses may have prerequisites, for example, if prerequisites[i] = [ai, bi] this means you must take the course bi before the course ai.

Given the total number of courses numCourses and a list of the prerequisite pairs, return the ordering of courses you should take to finish all courses.

If there are many valid answers, return any of them. If it is impossible to finish all courses, return an empty array.

Example:

Input: numCourses = 2, prerequisites = [[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].
Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
Output: [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].
Input: numCourses = 1, prerequisites = []
Output: [0]


Approach:  Basically if you see it is clear example of Topological Sorting. We just need to take care of cycles in the Graph as if there is a cycle exits that means all the course can't be finished. Here are the two steps which we need to take:

  1. Build a directed graph using prerequisites. If a prerequisite is [1, 0] that means there is an edge from a node with value 0 to node with value 1.
  2. Topological sort.

That's all!

 

Implementation in C#:

    public int[] FindOrder(int numCourses, int[][] prerequisites) 

    {

        var graph = this.BuildGraph(numCourses, prerequisites);

        bool[] visited = new bool[numCourses];

        Stack<int> stack = new Stack<int>();

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

        {

            if (!visited[i])

            {

                bool[] onCycle = new bool[numCourses];

                bool isCycle = false;

                this.TopologicalSort(i, graph, visited, stack, onCycle, ref isCycle);

                

                if (isCycle)

                {

                    return new int[] {};

                }

            }

        }        

        int[] courseOrder = new int[numCourses];

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

        {

            courseOrder[i] = stack.Pop();

        }        

        return courseOrder;

    }

    

    private void TopologicalSort(int course, Dictionary<int, List<int>> graph, bool[] visited, Stack<int> stack, bool[] onCycle, ref bool isCycle)

    {

        if (isCycle)

        {

            return;

        }

        visited[course] = true;

        onCycle[course] = true;        

        foreach (int child in graph[course])

        {

            if (onCycle[child])

            {

                isCycle = true;

                return;

            }            

            if (!visited[child])

            {

                this.TopologicalSort(child, graph, visited, stack, onCycle, ref isCycle);

            }

        }

        onCycle[course] = false;

        stack.Push(course);

    }

    

    private Dictionary<int, List<int>> BuildGraph(int numCourses, int[][] prerequisites)

    {

        var graph = this.InitializeGraph(numCourses);

        foreach (var prerequisite in prerequisites)

        {

            graph[prerequisite[1]].Add(prerequisite[0]);

        }

        return graph;

    }

    

    private Dictionary<int, List<int>> InitializeGraph(int numCourses)

    {

        var graph = new Dictionary<int, List<int>>();

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

        {

            graph[i] = new List<int>();

        }

        return graph;

    }


Complexity: O(V + E) where is V is the number of nodes and E is the number of Edges.


[Google Question][LeetCode] Count Univalue Subtrees

Problem: Given the root of a binary tree, return the number of uni-value subtrees. A uni-value subtree means all nodes of the subtree have the same value.

Example:

Input: root = [5,1,5,5,5,null,5]
Output: 4


Approach: The basic here is to understand what is a uni-value subtree. Given a node in the tree is a uni-value subtree if it meets one of the following criteria:

  • The node has no children.
  • All of the node's children are uni-value subtrees, and the node and its children all have the same value.

So if you see we need to check first if children are uni-value or not then only we can decide whether current node is uni-value or not. This tells us that we can use Post-Order traversal here to solve this question. The basic check is:

If Left subtree is uni-val and Right subtree is uni-val then a node is uni-val if node.Value == node.Left.Value == node.Right.Value.

With a few boundary condition check we are good here. You can look at the implementation to see the boundary conditions.


Implementation in C#:

    public int CountUnivalSubtrees() 

    {

        int count = 0;

        IsUnivalSubtree(this.Root, ref count);

        return count;

    }

    

    private bool IsUnivalSubtree(BinaryTreeNode node, ref int count)

    {

        if (node == null)

        {

            return true;

        }

        if (node.LeftNode == null && node.RightNode == null)

        {

            ++count;

            return true;

        }

        bool leftUnival = this.IsUnivalSubtree(node.LeftNode, ref count);

        bool rightUnival = this.IsUnivalSubtree(node.RightNode, ref count);

        if (leftUnival && rightUnival)

        {

            if (node.LeftNode != null && node.RightNode != null && node.LeftNode.Value != node.RightNode.Value)

            {

                return false;

            }     

            int compareVal = node.LeftNode?.Value ?? node.RightNode.Value;

            if (node.val == compareVal)

            {

                 ++count;

                return true;

            }   

        }

        return false;

    }


Complexity: O(n)

[LeetCode] Check If a String Contains All Binary Codes of Size K

Problem: Given a binary string s and an integer k. Return True if every binary code of length k is a substring of s. Otherwise, return False.

Example:

Input: s = "00110110", k = 2
Output: true
Explanation: The binary codes of length 2 are "00", "01", "10" and "11". They can be all found as substrings at indicies 0, 1, 3 and 2 respectively.
Input: s = "00110", k = 2
Output: true
Input: s = "0110", k = 1
Output: true
Explanation: The binary codes of length 1 are "0" and "1", it is clear that both exist as a substring. 
Input: s = "0110", k = 2
Output: false
Explanation: The binary code "00" is of length 2 and doesn't exist in the array.
Input: s = "0000000001011100", k = 4
Output: false


Approach: The intuition here is the number of k bit integers is 2^k. Now we just need to check if the number of unique substrings of size k in the given string is 2^k then we get all the binary codes otherwise if we didn't. Here is the algorithm look like:

  • HashSet set = {}
  • FOR i = 0 to n - k
    • set.Add(s.Substring(i, k))
  • RETURN Count(set) == 2 ^ k

The above approach will work correctly but it will be expensive as it will take O(n * k) time. Let's try to do better. The problem here getting substring itself take O(k) time. We can reduce this time. If you see it's kind of sliding window where at every step one character form start is getting removed from window and next character is getting added. 

We can basically calculate the decimal number formed by the first k binary characters and then we can use current decimal number to get the next decimal number. In the end if the number of unique formed decimal numbers is 2^k then we can return true otherwise false. Let's try to understand how to calculate the next decimal number given the current one using an example:

Say s = "00110" and k = 2

Max number formed by k (= 2) bits is (2^2 - 1 =) 3 (binary representation = 11). Let's call it maxKBitNumber.

First 2 binary characters will make the number 0 ("00"), call it currDecimalNumber.

Now the next character is '1'. Here is the formula which we can use: 

nextDecimalNumber = ((currDecimalNumber << 1) & maxKBitNumber) | (currChar - '0')

nextDecimalNumber  = ((0 << 1) & 3) | 1 = 1

Now next char is '1' and currDecimalNumber is also 1 so:

nextDecimalNumber  = ((1 << 1) & 3) | 1 = 3

Now the next char is '0' and currDecimalNumber is 3, so 

nextDecimalNumber  = ((3 << 1) & 3) | 0 = 2

Hopefully the approach is clear now.


Implementation in C#:

Approach 1: Using substrings:

    public bool HasAllCodes1(string s, int k) 

    {

        HashSet<string> binaryCodesSet = new HashSet<string>();    

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

        {

            binaryCodesSet.Add(s.Substring(i, k));

        }

        return (1 << k) == binaryCodesSet.Count;

    }


Approach 2: Using sliding window:

    public bool HasAllCodes(string s, int k) 

    {

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

        int numOfAllKBitIntegers = (1 << k);

        int maxKBitNum = numOfAllKBitIntegers - 1;

        int decimalVal = 0;

        // Getting the first k bit binary number

        for (int i = 0; i < k && i < s.Length; ++i)

        {

            decimalVal = ((decimalVal << 1) & maxKBitNum) | (s[i] - '0');

        }

        decimalValsSet.Add(decimalVal);

        for (int i = k; i < s.Length; ++i)

        {

            decimalVal = ((decimalVal << 1) & maxKBitNum) | (s[i] - '0');

            decimalValsSet.Add(decimalVal);

        }

        return numOfAllKBitIntegers == decimalValsSet.Count;

    }


Complexity: Approach 1: O(n *k), Approach 2: O(n)

Tuesday, March 9, 2021

[Google Question][LeetCode] Sort Transformed Array

Problem: Given a sorted array of integers nums and integer values a, b and c. Apply a quadratic function of the form f(x) = ax^2 + bx + c to each element x in the array.

The returned array must be in sorted order.

Expected time complexity: O(n)

Example:

Input: nums = [-4,-2,2,4], a = 1, b = 3, c = 5
Output: [3,9,15,33]
Input: nums = [-4,-2,2,4], a = -1, b = 3, c = 5
Output: [-23,-5,1,7]


Approach: If you see ax^2 + bx + c forms a parabola. We know that  ax 2  + bx + c, if a>0, the parabola opens up, that means The value at both ends is larger than the middle which indicates that we will get max value with either x = nums[0] or x = nums[n-1] and if a<0, the parabola opens downward, and the value at both ends is smaller than the middle which gives us hind that we will get minimum value at x = nums[0] or x = nums[n-1]. 

When a=0, it is a straight-line method, which is monotonically increasing or decreasing which in our case is increasing.

That's all!


Implementation in C#:

    public int[] SortTransformedArray(int[] nums, int a, int b, int c) 

    {

        int length = nums?.Length ?? 0;

        if (length == 0)

        {

            return new int[] {};

        }

        if (length == 1)

        {

            return new int[] { this.ParabolaFunction(nums[0], a, b, c) }; 

        }

        int low = 0, high = length - 1;

        int writeIndex = a >= 0 ? length - 1 : 0;

        int[] result = new int[length];

        while (low <= high)

        {

            int lowValue = this.ParabolaFunction(nums[low], a, b, c);

            int highValue = this.ParabolaFunction(nums[high], a, b, c);

            if (a >= 0)

            {

                if (lowValue < highValue)

                {

                    result[writeIndex] = highValue;

                    --high;

                }

                else

                {

                    result[writeIndex] = lowValue;

                    ++low;

                }

                --writeIndex;

            }

            else

            {

                if (lowValue < highValue)

                {

                    result[writeIndex] = lowValue;

                    ++low;

                }

                else

                {

                    result[writeIndex] = highValue;

                    --high;

                }

                ++writeIndex;

            }

        }

        return result;

    }

    

    private int ParabolaFunction(int x, int a, int b, int c)

    {

        return a * x * x + b * x + c;

    }


Complexity: O(n)

[Facebook Question] Missing Ranges

Problem: You are given an inclusive range [lower, upper] and a sorted unique integer array nums, where all elements are in the inclusive range.

A number x is considered missing if x is in the range [lower, upper] and x is not in nums.

Return the smallest sorted list of ranges that cover every missing number exactly. That is, no element of nums is in any of the ranges, and each missing number is in one of the ranges.

Each range [a,b] in the list should be output as:

"a->b" if a != b

"a" if a == b

Example:

Input: nums = [0,1,3,50,75], lower = 0, upper = 99
Output: ["2","4->49","51->74","76->99"]
Explanation: The ranges are:
[2,2] --> "2"
[4,49] --> "4->49"
[51,74] --> "51->74"
[76,99] --> "76->99"
Input: nums = [], lower = 1, upper = 2
Output: ["1->2]
Explanation: The only missing range is [1,2], which becomes "1->2".


Approach: We can simply scan the whole array and add the missing ranges in the result. Please note that:

  • if nums[i] - nums[i - 1] > 1 then we know the missing range is [nums[i-1] + 1, nums[i] - 1]

We also need to take care of the conditions where lower < nums[0] and upper > nums[length - 1].

That's all!


Implementation in C#:

    public IList<string> FindMissingRanges(int[] nums, int lower, int upper) 

    {

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

        int length = nums?.Length ?? 0;

        if (length == 0)

        {

            result.Add(this.GenerateRange(lower, upper));

            return result;

        }

        if (lower < nums[0])

        {

            result.Add(this.GenerateRange(lower, nums[0] - 1));

        }

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

        {

            if (nums[i] - nums[i - 1] > 1)

            {

                result.Add(this.GenerateRange(nums[i - 1] + 1, nums[i] - 1));

            }

        } 

        if (upper > nums[length - 1])

        {

            result.Add(this.GenerateRange(nums[length - 1] + 1, upper));

        }

        return result;   

    }

    

    private string GenerateRange(int low, int high)

    {

        string range = low.ToString();

        if (high > low)

        {

            range += "->" + high;

        }

        return range;

    }


Complexity: O(n)

[Facebook Question][LeetCode] Find K Closest Elements

Problem: Given a sorted integer array arr, two integers k and x, return the k closest integers to x in the array. The result should also be sorted in ascending order.

An integer a is closer to x than an integer b if:

|a - x| < |b - x|, or

|a - x| == |b - x| and a < b

Example:

Input: arr = [1,2,3,4,5], k = 4, x = 3
Output: [1,2,3,4]
Input: arr = [1,2,3,4,5], k = 4, x = -1
Output: [1,2,3,4]


Approach: We can use queue here. We can keep enqueuing the array elements till we have count = k. Now if we have count = k and a new elements come, we enqueue it only if this element is closer than the element at the queue's front. In that case we dequeue the front element from the queue too. In the end the elements in the queue is our answer. 

The above approach works well and will solve the problem in linear time. Let's try to do little better.

We can take the advantage of the fact that the array is sorted. We can use binary search to find the index where x should be placed (x could already in the array), say the index is target_index.

Once we have the target_index, we know that the closest k elements can be the left k elements or right k elements or mix of them so we target this range [target_index - k, target_index + k - 1] and find the closest elements in it.

That's all!


Implementation in C#:

Approach 1: Using queue:

    public IList<int> FindClosestElements(int[] arr, int k, int x) 

    {

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

        int length = arr?.Length ?? 0;

        if (length == 0 || k == 0 || k > length)

        {

            return result;

        }

        // 2D array -> [0]: index [1] distance

        Queue<int[]> queue = new Queue<int[]>();

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

        {

            if (queue.Count < k)

            {

                queue.Enqueue(new int[] {i, Math.Abs(x - arr[i])});

            }

            else

            {

                if (queue.Peek()[1] > Math.Abs(x - arr[i]))

                {

                    queue.Dequeue();

                    queue.Enqueue(new int[] {i, Math.Abs(x - arr[i])});

                }

            }

        }

        while (queue.Count > 0)

        {

            result.Add(arr[queue.Dequeue()[0]]);

        }

        return result;

    }


Approach 2: Using Binary Search:

    public IList<int> FindClosestElements(int[] arr, int k, int x) 

    {    

        int length = arr?.Length ?? 0;

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

        if (length == 0 || k == 0 || k > length)

        {

            return result;

        }

        if (x < arr[0])

        {

            this.AddArrayElementsToList(arr, result, 0, k - 1);

            return result;

        }

        if (x > arr[length - 1])

        {

            this.AddArrayElementsToList(arr, result, length - k, length - 1);

            return result;

        }

        int index = Array.BinarySearch(arr, x);

        // x does not exist

        if (index < 0)

        {

            index = ~index;

        }

        int low = Math.Max(0, index - k);

        int high = Math.Min(length - 1, index + k - 1);

        while (high - low + 1 > k)

        {

            if (x - arr[low] <= arr[high] - x)

            {

                --high;

            }

            else if (x - arr[low] > arr[high] - x)

            {

                ++low;

            }

        }

        this.AddArrayElementsToList(arr, result, low, high);

        return result;

    }

    

    private void AddArrayElementsToList(int[] arr, List<int> result, int left, int right)

    {

        for (int i = left; i <= right; ++i)

        {

            result.Add(arr[i]);

        }

    }


Complexity: Approach 1: O(n), Approach 2: O(logn + k)

[Facebook Question] Strobogrammatic Number

Problem: Given a string num which represents an integer, return true if num is a strobogrammatic number

A strobogrammatic number is a number whose numeral is rotationally symmetric, so that it appears the same when rotated 180 degrees. In other words, the numeral looks the same right-side up and upside down

Example:

Input: num = "9966"
Output: true
Input: num = "1001"
Output: true


Approach: We can simply maintain a hash table say Rotation_Map with key as number and value as the number which you will get after rotating it by 180 degree. Here are the pairs -

  • 0 -> 0
  • 1 -> 1
  • 6 -> 9
  • 8 -> 8
  • 9 -> 6

Now we will reverse the given string and call it say rev_string. Now for i = 0 to length - 1 we check if num[i] exist in the rotation table and if rev_string[i] is equal to Rotation_Map[num[i]]. At any point if we see this condition is not true then we can safely return false.

In the end if we find every character in input string satisfy the condition, we return true.

 

Implementation in C#:

        public static bool IsStrobogrammatic(string num)

        {

            Dictionary<char, char> rotationMap = new Dictionary<char, char>();

            rotationMap['0'] = '0';

            rotationMap['1'] = '1';

            rotationMap['6'] = '9';

            rotationMap['8'] = '8';

            rotationMap['9'] = '6';

            char[] revArr = num.ToCharArray();

            Array.Reverse(revArr);

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

            {

                if (rotationMap.ContainsKey(num[i]) )

                {

                    if (rotationMap[num[i]] != revArr[i])

                    {

                        return false;

                    }

                }

                else if (num[i] != revArr[i])

                {

                    return false;

                }

            }

            return true;

        }


Complexity: O(n)