Thursday, September 30, 2021

[LeetCode] Split Linked List in Parts

Problem: Given the head of a singly linked list and an integer k, split the linked list into k consecutive linked list parts.

The length of each part should be as equal as possible: no two parts should have a size differing by more than one. This may lead to some parts being null.

The parts should be in the order of occurrence in the input list, and parts occurring earlier should always have a size greater than or equal to parts occurring later.

Return an array of the k parts.

Example:

Input: head = [1,2,3], k = 5
Output: [[1],[2],[3],[],[]]
Explanation:
The first element output[0] has output[0].val = 1, output[0].next = null.
The last element output[4] is null, but its string representation as a ListNode is [].

Input: head = [1,2,3,4,5,6,7,8,9,10], k = 3
Output: [[1,2,3,4],[5,6,7],[8,9,10]]
Explanation:
The input has been split into consecutive parts with size difference at most 1, and earlier parts are a larger size than the later parts.

Constraints:

  • The number of nodes in the list is in the range [0, 1000].
  • 0 <= Node.val <= 1000
  • 1 <= k <= 50


Approach: It's a straight forward problem to solve. Please have a look at implementation to understand the approach.


Implementation in C#:

    public ListNode[] SplitListToParts(ListNode head, int k) 

    {

        ListNode[] result = new ListNode[k];

        int countOfNodes = this.Count(head);

        int groupSize = countOfNodes / k;

        int extraNodes = countOfNodes % k;

        ListNode node = head;

        for (int i = 0; i < k && node != null; ++i)

        {

            result[i] = node;

            for (int j = 1; j < groupSize + (extraNodes > 0 ? 1 : 0); ++j)

            {

                node = node.next;

            }

            ListNode nextNode = node.next;

            // break the link

            node.next = null;

            node = nextNode;

            --extraNodes;

        }        

        return result;

    }

    

    private int Count(ListNode head)

    {

        int count = 0;

        ListNode node = head;

        while (node != null)

        {

            ++count;

            node = node.next;

        }

        return count;

    }


Complexity: O(n)

[LeetCode] Partition to K Equal Sum Subsets

Problem: Given an integer array nums and an integer k, return true if it is possible to divide this array into k non-empty subsets whose sums are all equal.

Example:

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


Approach: First we will check if SUM(nums) mod k is 0 or not. If not then we can safely return false. 

Otherwise we have a target_sum which SUM(nums) / k. We just need to find k subsets of nums whose sum is k. This is a backtracking problem. You can understand the logic by just looking at the code.


Implementation in C#:

    public bool CanPartitionKSubsets(int[] nums, int k) 

    {

        int sum = 0;

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

        {

            sum += nums[i];

        }

        // Impossible case

        if (sum % k != 0)

        {

            return false;

        }

        int targetSum = sum / k;

        bool[] visited = new bool[nums.Length];

        return KPartitionsPossible(nums, k, targetSum, 0, 0, visited);

    }

    

    private bool KPartitionsPossible(

        int[] nums,

        int k, 

        int targetSum, 

        int currSum,

        int index,

        bool[] visited)

    {

        // All the subsets found, return true.

        if (k == 0)

        {

            return true;

        }

        // Get 1 more subset, now find k - 1 subsets

        if (currSum == targetSum)

        {

            return KPartitionsPossible(nums, k - 1, targetSum, 0, 0, visited);

        }

        for (int i = index; i < nums.Length; ++i)

        {

            if (!visited[i] && currSum + nums[i] <= targetSum)

            {

                visited[i] = true;

                // If true then done return true.

                if (KPartitionsPossible(nums, k, targetSum, currSum + nums[i], i + 1, visited))

                {

                    return true;       

                }

                visited[i] = false;

            }

        }        

        return false;

    }


Complexity: O(k * 2 ^ n)

Friday, September 24, 2021

[LeetCode] N-th Tribonacci Number

Problem: The Tribonacci sequence Tn is defined as follows: 

T0 = 0, T1 = 1, T2 = 1, and Tn+3 = Tn + Tn+1 + Tn+2 for n >= 0.

Given n, return the value of Tn.

Example:

Input: n = 4
Output: 4
Explanation:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4
Input: n = 25
Output: 1389537

Constraints:

  • 0 <= n <= 37
  • The answer is guaranteed to fit within a 32-bit integer, ie. answer <= 2^31 - 1.


Approach: The problem is just like finding nth fibonacci number. Here we just need to add 3 previous numbers. That's all!


Implementation in C#:

    public int Tribonacci(int n)
    {        
        if (n <= 1)
        {
            return n;
        }
        if (n == 2)
        {
            return 1;
        }
        int t0 = 0, t1 = 1, t2 = 1;
        for (int i = 3; i <= n; ++i)
        {
            int t = t0 + t1 + t2;
            t0 = t1;
            t1 = t2;
            t2 = t;
        }
        return t2;
    }


Complexity: O(n)

Thursday, September 23, 2021

[LinkedIn] Shortest Word Distance

Problem: Given a list of words and two words word1 and word2, return the shortest distance between these two words in the list.

Example:

Input: words = ["practice","makes","perfect","coding","makes"], word1 = "coding", word2 = "practice"
Output: 3
Explanation: IndexOf("coding") - IndexOf("practice") = 3 - 0 = 3
Input: words = ["practice","makes","perfect","coding","makes"], word1 = "makes", word2 = "coding"
Output: 1
Explanation: There are two places where "makes" is present in the array which is 1 and 4. The index of "coding" is 3 so the differences are 3 - 1 = 2 and 4 - 3 = 1. The minimum of theses distances (2, 1) is 1.

Constraints:

  • word1 is not equal to word2
  • word1 and word2 both are present in the given words array.


Approach: It's a straight forward problem to solve. You can look at the implementation to understand the approach.


Implementation in Java:

    public int shortestDistance(String[] words, String word1, String word2) 

    {

        int indexOfWord1 = -1;

        int indexOfWord2 = -1;

        int minDistance = Integer.MAX_VALUE;

        for (int i = 0; i < words.length; ++i) {

            if (words[i].equals(word1))

            {

                indexOfWord1 = i;

                if (indexOfWord2 != -1) {

                    minDistance = Math.min(minDistance, Math.abs(indexOfWord2 - indexOfWord1));

                }

            }

            if (words[i].equals(word2)) {

                indexOfWord2 = i;

                if (indexOfWord1 != -1) {

                    minDistance = Math.min(minDistance, Math.abs(indexOfWord2 - indexOfWord1));

                }

            }

        }

        return minDistance;

    }


Complexity: O(n)

[Google] Minimize Max Distance to Gas Station

Problem: On a horizontal number line, we have gas stations at positions stations[0], stations[1], ..., stations[N-1], where N = stations.length.

Now, we add K more gas stations so that D, the maximum distance between adjacent gas stations, is minimized.

Return the smallest possible value of D.

Example:

Input: stations = [1,2,3,4,5,6,7,8,9,10], K = 9
Output: 0.5
Explanation: All possible concatenations are "","un","iq","ue","uniq" and "ique".
Maximum length is 4.

Constraints:

  • stations.length will be an integer in range [10, 2000].
  • stations[i] will be an integer in range [0, 10^8].
  • K will be an integer in range [1, 10^6].
  • Answers within 10^-6 of the true value will be accepted as correct.


Approach: We are going to use binary search here. How? What is our sorted array here, it is given as input that is our stations distance array. What is initial left? left is the lowest distance possible which is 0. Now  what is initial right; The right will be the maximum distance possible in the given array which is last station distance - first station distance. 

Now we will take the mid of left and right distance and see how many stations can be placed if we use mid distance. If number of station can be placed is <= k then we know that we can make the distance less so we will make right = mid and otherwise if the stations placed are more than k then we have to make distance more i.e. we need to make left = mid.

In the end we can return left or right.

 

Implementation in Java:

    public double minmaxGasDist(int[] stations, int k) {

        int length = stations.length;

        double left = 0;

        double right = stations[length - 1] - stations[0];

        while (right - left > 1e-6) {

            double mid = left + (right - left) / 2;

            if (isFeasible(stations, k, mid)) {

                right = mid;

            }

            else {

                left = mid;

            }

        }

        return right;

    }


    private boolean isFeasible(int[] stations, int k, double dist) {

        int count = 0;

        for (int i = 1; i < stations.length; ++i) {

            count += (int)((stations[i] - stations[i - 1]) / dist);

        }

        return count <= k;

    }


Complexity: O(nlogD) where D is the difference between station[n-1] and stations[0].

[LeetCode] Break a Palindrome

Problem: Given a palindromic string of lowercase English letters palindrome, replace exactly one character with any lowercase English letter so that the resulting string is not a palindrome and that it is the lexicographically smallest one possible.

Return the resulting string. If there is no way to replace a character to make it not a palindrome, return an empty string.

A string a is lexicographically smaller than a string b (of the same length) if in the first position where a and b differ, a has a character strictly smaller than the corresponding character in b. For example, "abcc" is lexicographically smaller than "abcd" because the first position they differ is at the fourth character, and 'c' is smaller than 'd'.

Example:

Input: palindrome = "abccba"
Output: "aaccba"
Explanation: There are many ways to make "abccba" not a palindrome, such as "zbccba", "aaccba", and "abacba". Of all the ways, "aaccba" is the lexicographically smallest.
Input: palindrome = "a"
Output: ""
Explanation: There is no way to replace a single character to make "a" not a palindrome, so return an empty string.
Input: palindrome = "aa"
Output: "ab"
Input: palindrome = "aba"
Output: "abb"

Constraints:

  • 1 <= palindrome.length <= 1000
  • palindrome consists of only lowercase English letters.


Approach: Let's go greedy. We will make the first character 'a' which is not 'a' and in that way we will have a string which is not palindrome and also it will be the smallest string possible. Now what if the string have all 'a'. In that case we can change the last character to 'b'. The only problem with this approach will be if a string has all 'a's except the one character. For example "aba", here if we change the first non 'a' character the string will become "aaa".

To solve this problem we either traverse the whole string to see if now the string has all 'a's  or we can just check the non 'a' character in first half of the string as the string is palindrome, the last half will have the exact same characters. 

 

Implementation in C#:

    public string BreakPalindrome(string palindrome) 

    {

        if(palindrome.Length <= 1)

        {

            return "";

        }        

        char[] palindromeArr = palindrome.ToCharArray();

        for (int i = 0; i < palindromeArr.Length / 2; ++i)

        {

            if (palindromeArr[i] != 'a')

            {

                palindromeArr[i] = 'a';

                return new string(palindromeArr);

             }

        }

        palindromeArr[palindromeArr.Length - 1] = 'b';

        return new string(palindromeArr);

    }


Complexity: O(n)

[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)

Tuesday, September 21, 2021

[LeetCode] Expression Add Operators

Problem: Given a string num that contains only digits and an integer target, return all possibilities to add the binary operators '+', '-', or '*' between the digits of num so that the resultant expression evaluates to the target value.

Example:

Input: num = "123", target = 6
Output: ["1*2*3","1+2+3"]
Input: num = "232", target = 8
Output: ["2*3+2","2+3*2"]
Input: num = "105", target = 5
Output: ["1*0+5","10-5"]
Input: num = "00", target = 0
Output: ["0*0","0+0","0-0"]
Input: num = "3456237490", target = 9191
Output: []


Approach: This is a backtracking problem (with no shortcuts :)). Here we will try every combination that is to put +, - and * operators and see if the calculated value is the target or not. This problems looks simple initially but there are problems:

We can combine multiple digits to make one operand like in the third example from "105" we have one combination which 10 - 5.  How to deal with this? I would say we will make it our 4th operator and this operator works like follows:

current_operand = current_operand * 10 + num[current_index]

We just need to take care of numbers like "05" etc.

The second problem is because of  '*' operation, we just can't calculate expression value on the fly like (1 + 2 * 3), if we try to do it on the fly then we will be calculating something like following:

1 + 2 = 3

3 * 3 = 9

The above calculation is clearly wrong as the expression value is 7. How do we calculate it, then?

To do this we need to keep track of the previous operand. Let's see how it helps:

0 + 1 =  1                                         | prev = +1

1 + 2 = 3                                          | prev = +2

1 + 2 * 3 = (3 - 2) + ( 2 * 3) = 7      | prev = +6

So the formula is (current_result - previous_operand) + (previous_operand * current_operand)

Let's see with another example 4 * 2 - 10 * 3:

0 + 4 = 4                                                                         | prev = +4

4 * 2 = (4 - 4) + ( 4 * 2) = 8                                           | prev = +8

4 * 2 - 10 = 8 - 10 = -2                                                   | prev = -10

4 * 2 - 10 * 3 = (-2 - (-10) + (-10 * 3) = 8 - 30 = 22      | prev = -30

Now we have dealt with both the problems and we are ready for our implementation.


Implementation in C#:

    public IList<string> AddOperators(string num, int target) 

    {

        if (num.Length == 0)

        {

            return new List<string>();

        }    

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

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

        this.AddOperatorsRecurse(num, target, result, 0, 0, 0, 0, ops);

        return result;

    }

    

    public void AddOperatorsRecurse(

        string num, 

        int target,

        List<string> result,

        int currIndex, 

        long currOperand, 

        long prevOperand, 

        long currResult, 

        List<string> ops)

    {

        if (currIndex == num.Length)

        {

            if (currResult == target && currOperand == 0)

            {

                StringBuilder sb = new StringBuilder();

                for (int i = 1; i < ops.Count; ++i)

                {

                    sb.Append(ops[i]);

                }

                

                result.Add(sb.ToString());

            }

            return;

        }

        // Add next digit to current operand

        currOperand = currOperand * 10 + long.Parse(num[currIndex].ToString());

        // To take care of 05,00 etc

        if (currOperand > 0)

        {

            this.AddOperatorsRecurse(num, target, result, currIndex + 1, currOperand, prevOperand, currResult, ops);

        }

        // Add

        ops.Add("+");

        ops.Add(currOperand.ToString());

        this.AddOperatorsRecurse(

            num, 

            target, 

            result, 

            currIndex + 1, 

            0, 

            currOperand, 

            currResult + currOperand, 

            ops);

        ops.RemoveAt(ops.Count - 1);

        ops.RemoveAt(ops.Count - 1);

        if (ops.Count > 0)

        {

            // Subtract

            ops.Add("-");

            ops.Add(currOperand.ToString());

            this.AddOperatorsRecurse(

                num, 

                target, 

                result, 

                currIndex + 1, 

                0, 

                -currOperand, 

                currResult - currOperand, 

                ops);

            ops.RemoveAt(ops.Count - 1);

            ops.RemoveAt(ops.Count - 1);  

            // Multiplication

            ops.Add("*");

            ops.Add(currOperand.ToString());

            this.AddOperatorsRecurse(

                num, 

                target, 

                result, 

                currIndex + 1, 

                0, 

                currOperand * prevOperand, 

                currResult - prevOperand + (currOperand * prevOperand), 

                ops);

            ops.RemoveAt(ops.Count - 1);

            ops.RemoveAt(ops.Count - 1);

        }

    }


Complexity: Given we have 4 recursive paths(operators) and every recursive path ends when index reached to the end of the string, our complexity is 4^n. We are also joining the whole ops which can be of  2 * n size at worse so our complexity is O(n * 4^n)

[Microsoft][LeetCode] Distinct Subsequences

Problem: Given two strings s and t, return the number of distinct subsequences of s which equals t.

A string's subsequence is a new string formed from the original string by deleting some (can be none) of the characters without disturbing the remaining characters' relative positions. (i.e., "ACE" is a subsequence of "ABCDE" while "AEC" is not).

It is guaranteed the answer fits on a 32-bit signed integer.

Example:

Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from S.
rabbbit
rabbbit
rabbbit
Input: s = "babgbag", t = "bag"
Output: 5
Explanation:
As shown below, there are 5 ways you can generate "bag" from S.
babgbag
babgbag
babgbag
babgbag
babgbag


Approach: We are going to use DP here. We are going to maintain a 2D table where Table[i][j] will tell the number of subsequences of s[0...j] of t[0...i]. Obviously Table[Length(t)][Length(s)] will be our answer. Here is how we will fill the table:

  • We take the 2D Table of size Table[Length(t) + 1][Length(s + 1]
  • For all i = 0...Length(s): Table[0][i] = 1 as if t is empty that means for s[0...i] will have exactly 1 number os subsequence which is equal to t.
  • Table[i][j] = 
    • Table[i][j - 1] if t[i - 1] != s[j - 1] 
    • Table[i - 1][j - 1] + Table[i][j - 1] otherwise.

That's all!

 

Implementation in C#:

    public int NumDistinct(string s, string t) 

    {

        int[,] table = new int[t.Length + 1, s.Length + 1];

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

        {

            table[0, i] = 1;

        }

        for (int i = 1; i <= t.Length; ++i)

        {

            for (int j = 1; j <= s.Length; ++j)

            {

                if (t[i - 1] == s[j - 1])

                {

                    table[i, j] = table[i - 1, j - 1] + table[i, j - 1];

                }

                else

                {

                    table[i, j] = table[i, j - 1];

                }

            }

        }

        return table[t.Length, s.Length];

    }


Complexity: O(m * n)

Monday, September 20, 2021

[LeetCode] Find Winner on a Tic Tac Toe Game

Problem: Tic-tac-toe is played by two players A and B on a 3 x 3 grid.

Here are the rules of Tic-Tac-Toe:

  • Players take turns placing characters into empty squares (" ").
  • The first player A always places "X" characters, while the second player B always places "O" characters.
  • "X" and "O" characters are always placed into empty squares, never on filled ones.
  • The game ends when there are 3 of the same (non-empty) character filling any row, column, or diagonal.
  • The game also ends if all squares are non-empty.
  • No more moves can be played if the game is over.

Given an array moves where each element is another array of size 2 corresponding to the row and column of the grid where they mark their respective character in the order in which A and B play.

Return the winner of the game if it exists (A or B), in case the game ends in a draw return "Draw", if there are still movements to play return "Pending".

You can assume that moves is valid (It follows the rules of Tic-Tac-Toe), the grid is initially empty and A will play first.

Example:

Input: moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
Output: "A"
Explanation: "A" wins, he always plays first.
"X  "    "X  "    "X  "    "X  "    "X  "
"   " -> "   " -> " X " -> " X " -> " X "
"   "    "O  "    "O  "    "OO "    "OOX"
Input: moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]
Output: "B"
Explanation: "B" wins.
"X  "    "X  "    "XX "    "XXO"    "XXO"    "XXO"
"   " -> " O " -> " O " -> " O " -> "XO " -> "XO " 
"   "    "   "    "   "    "   "    "   "    "O  "
Input: moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]
Output: "Draw"
Explanation: The game ends in a draw since there are no moves to make.
"XXO"
"OOX"
"XOX"
Input: moves = [[0,0],[1,1]]
Output: "Pending"
Explanation: The game has not finished yet.
"X  "
" O "
"   "

Constraints:

  • 1 <= moves.length <= 9
  • moves[i].length == 2
  • 0 <= moves[i][j] <= 2
  • There are no repeated elements on moves.
  • moves follow the rules of tic tac toe.


Approach: The simplest way is to create a 2D array and then check for all the patterns but this will be expensive (m * n) both in terms of time and space. Let's try to do better. 

Let's define player A value is 1 and player B value is -1. We can take any value but this is just intuitive. Now let's see what are the winning combination of player A:

  1. 'X' in all cells of a given row 
  2. 'X' in all cells of a given column 
  3. 'X' in all cells of diagonal
  4. 'X in all cells of anti diagonal

That means if value of player A is 1. If the sum of any row or col or diagonal is 3 then player A is winner and similarly if the sum is -3 here then winner is player B.

We can take 2 1D arrays say rows and cols of size 3 (grid dimension) for maintaining the sums on every row and column. We can take two variables say diag1 and diag2 to maintain the sum on diagonal and anti diagonal.

With this our algorithm looks like:

  • FOREACH move in moves
    • row = move[0]
    • col = move[1]
    • rows[row] = rows[row] + playerVal
    • cols[col] = cols[col] + playerVal
    • IF row == col // On Diagonal
      • diag1 = diag1 + playerVal
    • IF row + col == gridDimension - 1 // On Anti Diagonal
      • diag2 = diag2 + playerVal
    • Here we check for our winner and return if any
    • Change the playerVal, next turn
  • Here we check if it is a draw or pending

That's all!


Implementation in C#:

    public string Tictactoe(int[][] moves) 

    {

        int gridDimension = 3;

        int[] rows = new int[gridDimension];

        int[] cols = new int[gridDimension];

        int valAtDiag1 = 0, valAtDiag2 = 0;

        int player = 1;

        foreach (int[] move in moves)

        {

            int moveRow = move[0];

            int moveCol = move[1];

            rows[moveRow] += player;

            cols[moveCol] += player;

            // Diagonal

            if (moveRow == moveCol)

            {

                valAtDiag1 += player;

            }

            // Anti Diagonal

            if (moveRow + moveCol == gridDimension - 1)

            {

                valAtDiag2 += player;

            }

            // Checking for winner

            if (Math.Abs(rows[moveRow]) == gridDimension 

                || Math.Abs(cols[moveCol]) == gridDimension 

                || Math.Abs(valAtDiag1) == gridDimension 

                || Math.Abs(valAtDiag2) == gridDimension)

            {

                return player == 1 ? "A" : "B";

            } 

            // Next player's turn

            player *= -1;

        }

        return moves.Length == gridDimension * gridDimension ? "Draw" : "Pending";

    }


Complexity: O(n)

Friday, September 17, 2021

[LeetCode] Longest Turbulent Subarray

Problem: Given an integer array arr, return the length of a maximum size turbulent subarray of arr.

A subarray is turbulent if the comparison sign flips between each adjacent pair of elements in the subarray.

More formally, a subarray [arr[i], arr[i + 1], ..., arr[j]] of arr is said to be turbulent if and only if:

  • For i <= k < j:
    • arr[k] > arr[k + 1] when k is odd, and
    • arr[k] < arr[k + 1] when k is even.
  • Or, for i <= k < j:
    • arr[k] > arr[k + 1] when k is even, and
    • arr[k] < arr[k + 1] when k is odd.

Example:

Input: arr = [9,4,2,10,7,8,8,1,9]
Output: 5
Explanation: arr[1] > arr[2] < arr[3] > arr[4] < arr[5]
Input: arr = [4,8,12,16]
Output: 2
Input: arr = [100]
Output: 1


Approach: The approach is same as the approach which we have taken to solve the problem of finding the sub array with maximum sum in the array i.e. Kadane algorithm. There we reset the current window when we find the current window's sum is less than 0, here we will reset the window when our pattern is broken. That's all!


Implementation in C#:

    public int MaxTurbulenceSize(int[] arr) 

    {

        int length = arr?.Length ?? 0;

        if (length <= 1)

        {

            return length;

        }

        int start = 0;

        int maxLength = 1;

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

        {

            int currCompare = arr[i - 1].CompareTo(arr[i]);

            if (currCompare == 0)

            {

                start = i;

            }

            else if (i == length - 1 || currCompare * arr[i].CompareTo(arr[i + 1]) != -1)

            {

                maxLength = Math.Max(maxLength, i - start + 1);

                start = i;

            }

        }

        return maxLength;

    }


Complexity: O(n)

[LeetCode] Intersection of Two Arrays II

Problem: Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear as many times as it shows in both arrays and you may return the result in any order.

Example:

Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2,2]
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [4,9]
Explanation: [9,4] is also accepted.


Approach: We can sort both the arrays and then we can use two pointers approach. The algorithm looks like follows:

  • Sort(num1)
  • Sort(num2)
  • result = []
  • i = 0, j = 0, k = 0
  • WHILE i < COUNT(num1) AND j < COUNT(num2)
    • IF num1[i] < num2[j]
      • i = i + 1
    • ELSE IF num1[i] > num2[j]
      • j = j + 1
    • ELSE
      • result[k] = num1[i]
      • i = i + 1
      • j = j + 1
      • k = k + 1
  • RETURN result

This approach will take O(nlogn) time but is very useful and fast if arrays are sorted

Another approach would be to simply use the hashing here to solve this question. You can look at the implementation to understand the approach.


Implementation in C#:

    public int[] Intersect(int[] nums1, int[] nums2) 

    {

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

        int[] arrayToBuildHash = nums1.Length < nums2.Length ? nums1 : nums2;

        int[] arrayToIterate = nums1.Length < nums2.Length ? nums2 : nums1;

        this.FillNumsWithCount(arrayToBuildHash, frequencies);

        return this.GetIntersection(arrayToIterate, frequencies);

    }

    

    private int[] GetIntersection(int[] nums, Dictionary<int, int> frequencies)

    {

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

        foreach(int num  in nums)

        {

            if (frequencies.ContainsKey(num))

            {

                commonNums.Add(num);

                --frequencies[num];

                if (frequencies[num] == 0)

                {

                    frequencies.Remove(num);

                }

            }

        }

        return commonNums.ToArray();

    }

    

    private void FillNumsWithCount(int[] nums, Dictionary<int, int> frequencies)

    {

        foreach(int num in nums)

        {

            if (!frequencies.ContainsKey(num))

            {

                frequencies[num] = 0;

            }

            ++frequencies[num];

        }

    }


Complexity: O(m + n)

Thursday, September 9, 2021

[Google Question][LeetCode] Sum of Distances in Tree

Problem: There is an undirected connected tree with n nodes labeled from 0 to n - 1 and n - 1 edges.

You are given the integer n and the array edges where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the tree.

Return an array answer of length n where answer[i] is the sum of the distances between the ith node in the tree and all other nodes.

Example:

Input: n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Output: [8,12,6,10,10,10]
Explanation: The tree is shown above.
We can see that dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5)
equals 1 + 1 + 2 + 2 + 2 = 8.
Hence, answer[0] = 8, and so on.

Input: n = 1, edges = []
Output: [0]

Input: n = 2, edges = [[1,0]]
Output: [1,1]


Approach: We can apply BFS taking every node as root and we can get our answer but this will be expensive solution as it will take O(n^2) time. Let's try to optimize it.

Let's say our tree is:

        0

      /.   \

    1.       2

  /         /    \ 

5.        3      4

Now let's say we calculated the distance for 0 which will be 8. Now let's try to calculate the distance of 1 -

     1

  /.      \

5.          0 [distance is 8]

                \

                   2

                 /.    \

               3        4

Here we already know that the sum of distances from 0 to every every node is 8. We can divide the this value into two part:

  1. distances of 0 to 1 and its children 
  2. distances of 0 to 2 and its children

Now when we calculate the same distances 1, we can say the sum of distances from 1 is going to be:

distances[0] - Number of Nodes in subtree with root as 1 + Number of nodes in subtree with root as 2 + 1 (for 0) = 

distance[0] - Number of Nodes in subtree with root as 1 + TotalNodes in tree - Number of Nodes in subtree with root as 1

Why? If you see for each node in the subtree(1), we are reducing the distance by 1 because instead of parent of 1 that is 0, we are now starting from 1. Similarly for each node in the subtree(2), we are adding 1 in the distance because instead of starting from 0, we are starting from the other 1, something like:

1 - 0.- 2 <

so the distance we need to add is the distance between 1 - 0 that is 1 to reach 2 and its children as there is no other way to reach subtree(2) from 1. If it is understood than we can have the generic formula:

distance[node] = distance[parentOfNode] - NumOfChildren[node] + (NumNodes - NumOfChildren[node])

We can use post order traversal to calculate the number of nodes in each subtree and initial distance calculation then we can use pre order traversal to calculate the final distances. Have a look at the implementation for more details.

That's all!


Implementation in C#:

public class Graph

{

    public int[] SumOfDistancesInTree(int n, int[][] edges) 

    {

        if (n == 1)

        {

            return new int[n];    

        }

        this.InitializeMembers(n);

        this.CreateGraph(edges);

        this.GetChildrenCountAndInitialResult(0, -1);   

        this.GetDistances(0, -1);

        return this.dist;

    }

    

    private void GetDistances(int node, int parent)

    {

        foreach (int child in this.graph[node])

        {

            // Bidirectional graph instead of visit set we can use this condition here.

            if (child == parent)

            {

                continue;

            }

            dist[child] = dist[node] - this.numOfChildren[child] + (this.numOfNodes - this.numOfChildren[child]);         

            this.GetDistances(child, node);

        }

    }

    

    private void GetChildrenCountAndInitialResult(int node, int parent)

    {

        foreach (int child in this.graph[node])

        {

            // Bidirectional

            if (child == parent)

            {

                continue;

            }

            this.GetChildrenCountAndInitialResult(child, node);

            this.numOfChildren[node] += this.numOfChildren[child];

            this.dist[node] += (this.dist[child] + this.numOfChildren[child]);

        }

        ++this.numOfChildren[node];

    }

    

    private void CreateGraph(int[][] edges)

    {

        foreach (int[] edge in edges)

        {

            this.graph[edge[0]].Add(edge[1]);

            this.graph[edge[1]].Add(edge[0]);

        }

    }

    

    private void InitializeMembers(int n)

    {

        this.numOfNodes = n;

        this.numOfChildren = new int[n];

        this.dist = new int[n];

        this.graph = new HashSet<int>[n];

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

        {

            this.graph[i] = new HashSet<int>();

        }

    }

    

    private HashSet<int>[] graph;

    private int[] numOfChildren;

    private int[] dist;

    private int numOfNodes;

}


Complexity: O(n)

[LeetCode] Largest Plus Sign

Problem: You are given an integer n. You have an n x n binary grid grid with all values initially 1's except for some indices given in the array mines. The ith element of the array mines is defined as mines[i] = [xi, yi] where grid[xi][yi] == 0.

Return the order of the largest axis-aligned plus sign of 1's contained in grid. If there is none, return 0.

An axis-aligned plus sign of 1's of order k has some center grid[r][c] == 1 along with four arms of length k - 1 going up, down, left, and right, and made of 1's. Note that there could be 0's or 1's beyond the arms of the plus sign, only the relevant area of the plus sign is checked for 1's.

Example:

Input: n = 5, mines = [[4,2]]
Output: 2
Explanation: In the above grid, the largest plus sign can only be of order 2. One of them is shown.

Input: n = 1, mines = [[0,0]]
Output: 0
Explanation: There is no plus sign, so return 0.


Approach: The brute force approach would be to take every cell as center and calculate the length of biggest plus it can produce. The maximum of these lengths will be our answer but this will take O(n^3) time. Let's try to optimize it.

We can use DP here. Let's see at given cell (i, j) what could be the longest plus size:

Min(Size of contiguous 1's on the left, Size of contiguous 1's on the right, Size of contiguous 1's down side, Size of contiguous 1's up)

Now we just need to calculate the max of above for calculation for every cell. Now we need to see how we can calculate the left/right/up/down contiguous 1s efficiently. We can use DP here to calculate it in O(n^2). We can maintain 4 tables each for left, right, up and down and we can fill it in following way:

  • Left[i][j] = if matrix[i][j] == 0 then 0 else Left[i][j-1]  + 1
  • Right[i][j] = if matrix[i][j] == 0 then 0 else Right[i][j+1] + 1
  • Down[i][j] = if matrix[i][j] == 0 then 0 else Down[i-1][j] + 1
  • Up[i][j] = if matrix[i][j] == 0 then 0 else Up[i+1][j] + 1

Now once we calculate it we can go at every cell (i, j) calculate the min of Left[i][j], Right[i][j], Down[i][j] and Up[i][j] say minVal[i][j]. In the end we can return max of all the minVal[i][j]s.

The above approach will solve the problem in O(n^2) and will work well but as you can see we are taking four 2D tables. We can apply the same algorithm using one 2D table only. The steps remains almost same and you can understand it by just looking at the implementation of our approach 2.


Implementation in C#:

Approach 1: Using four tables:

    public int OrderOfLargestPlusSign(int n, int[][] mines) 

    {

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

        foreach (int[] mine in mines)

        {

            minesSet.Add(mine[0] * n + mine[1]);

        }

        int[,] leftTable = new int[n,n];

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

        {

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

            {

                if (j == 0)

                {

                    leftTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    leftTable[i, j] = minesSet.Contains(i * n + j) ? 0 : leftTable[i, j - 1] + 1;

                }

            }

        }

        int[,] rightTable = new int[n,n];

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

        {

            for (int j = n - 1; j >= 0; --j)

            {

                if (j == n - 1)

                {

                    rightTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    rightTable[i, j] = minesSet.Contains(i * n + j) ? 0 : rightTable[i, j + 1] + 1;

                }

            }

        }

        int[,] downTable = new int[n,n];

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

        {

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

            {

                if (i == 0)

                {

                    downTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    downTable[i, j] = minesSet.Contains(i * n + j) ? 0 : downTable[i - 1, j] + 1;

                }

            }

        }

        int[,] upTable = new int[n,n];

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

        {

            for (int i = n - 1; i >= 0; --i)

            {

                if (i == n - 1)

                {

                    upTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    upTable[i, j] = minesSet.Contains(i * n + j) ? 0 : upTable[i + 1, j] + 1;

                }

            }

        }

        int maxLength = 0;

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

        {

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

            {   

                int currLength = this.MinElement(leftTable[i, j], rightTable[i, j], downTable[i, j], upTable[i, j]);

                maxLength = Math.Max(maxLength, currLength);

            }

        }        

        return maxLength;

    }


    private int MinElement(params int[] nums)

    {

        return nums.Min();

    }


Approach 2: Using only one table:

    public int OrderOfLargestPlusSign(int n, int[][] mines) 

    {

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

        foreach (int[] mine in mines)

        {

            // We can use it as matrix size is nxn

            minesSet.Add(mine[0] * n + mine[1]);

        }

        

        int[,] table = new int[n,n];

        int maxLength = 0, count;

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

        {

            count = 0;

            // left

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

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i,j] = count;

            }

            // right

            count = 0;

            for (int j = n - 1; j >= 0; --j)

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i, j] = Math.Min(table[i, j], count);

            }

        }

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

        {

            // down

            count = 0;

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

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i, j] = Math.Min(table[i, j], count);                

            }

            //up

            count = 0;

            for (int i = n - 1; i >= 0; --i)

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i, j] = Math.Min(table[i, j], count);

                if (table[i, j] > maxLength)

                {

                    maxLength = table[i, j];

                }

            }

        }  

        return maxLength;

    }


Complexity: O(n^2)

Wednesday, September 8, 2021

[LeetCode] Shifting Letters

Problem: You are given a string s of lowercase English letters and an integer array shifts of the same length.

Call the shift() of a letter, the next letter in the alphabet, (wrapping around so that 'z' becomes 'a').

For example, shift('a') = 'b', shift('t') = 'u', and shift('z') = 'a'.

Now for each shifts[i] = x, we want to shift the first i + 1 letters of s, x times.

Return the final string after all such shifts to s are applied.

Example:

Input: s = "abc", shifts = [3,5,9]
Output: "rpl"
Explanation: We start with "abc".
After shifting the first 1 letters of s by 3, we have "dbc".
After shifting the first 2 letters of s by 5, we have "igc".
After shifting the first 3 letters of s by 9, we have "rpl", the answer.
Input: s = "aaa", shifts = [1,2,3]
Output: "gfd"
Constraints:
  • 1 <= s.length <= 105
  • s consists of lowercase English letters.
  • shifts.length == s.length
  • 0 <= shifts[i] <= 109

Approach: The approach is very much straight forward. The only problem is while shifting characters we need to take care of a case where s[i] + shifts[i] is more than the value of 'z'. We can take care of this problem by using mod 26.


Implementation in C#:

    public string ShiftingLetters(string s, int[] shifts) 

    {

        int length = shifts.Length;

        StringBuilder stringBuilder = new StringBuilder();

        shifts[length - 1] %= 26;

        for (int i = length - 2; i >= 0; --i)

        {

            shifts[i] = (shifts[i + 1] + shifts[i]) % 26;

        }

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

        {

            int pos = s[i] - 'a';

            stringBuilder.Append((char)(((pos + shifts[i]) % 26) + 'a'));

        }    

        return stringBuilder.ToString();

    }


Complexity: O(n)