Tuesday, December 8, 2020

Leetcode discuss: 1499. Max Value of Equation

 Here is the link. 

First practice - C# - Deque - TLE 64/65

Dec. 7, 2020
I studied the code shared by votrubac. I came cross TLE error on test case 64/65.

First practice
It is the simple algebra. I do have to refresh my memory and then find what to look for. I like to borrow analysis from Lee215 in the following:

Explanation
Because xi < xj,
yi + yj + |xi - xj| = (yi - xi) + (yj + xj)

So for each pair of (xj, yj),
we have xj + yj, and we only need to find out the maximum yi - xi.
To find out the maximum element in a sliding window,
we can use priority queue or stack.

Analysis
The maximum yi - xi from index = 0 to i. For next point j > i,
get the maximum yi - xi within reach (xj - xi <= k).

Check if yi - xi + yj + xj produces the biggest result so far.

Adjust maximum using the new point: yj - xj.

Solution
One of ideas is to use the sliding window technique and monotonically decreasing deque. The maximum yi - xi therefore will be in the front. For the deque, the index j is stored.

Items that got out of the xj - xi window from the front of the queue should be removed. Afterwards, do some calculation. Finally, i is added to the deque, making sure we keep monotonicity of y - x. More detail, all elements with smaller y - x from the back of the deque are removed.

public class Solution {
    public int FindMaxValueOfEquation(int[][] points, int k) {
        var deque = new LinkedList<int>();

            var result = Int32.MinValue;
            var rows = points.Length;
            var columns = points[0].Length; 

            for (int j = 0; j < rows; ++j) 
            {
                while (deque.Count > 0 && (points[j][0] - points[deque.First()][0]) > k)
                {
                    deque.RemoveFirst();
                }

                if (deque.Count > 0)
                {
                    var first = deque.First();
                    result = Math.Max(result, points[first][1] - points[first][0] + points[j][1] + points[j][0]);
                }

                while (deque.Count > 0 && points[deque.Last()][1] - points[deque.Last()][0] < points[j][1] - points[j][0])
                {
                    deque.RemoveLast();
                }

                deque.AddLast(j);
            }    

            return result;
    }
}


Leetcode discuss: 1231. Divide Chocolate

 Here is the link. 

First practice - C# - Binary Search - Deadloop concern

Dec. 7, 2020
Introduction
It took me at least 10 minutes to understand what is asking for. I like to rephrase the requirement and see if I can figure out next time in less than one minutes.

Requirement statement
Input: sweetness = [1,2,3,4,5,6,7,8,9], K = 5
Output: 6
Explanation: You can divide the chocolate to [1,2,3], [4,5], [6], [7], [8], [9]

You will cut the chocolate in K + 1 pieces, any piece should include one or more than one consecutive numbers in the array. You will take the minimum sweatness piece, and you will try to make your piece maximum.

What is your maximum sweetness?

Test case deadloop
I ran into test case deadloop for the test caes [1, 2, 3, 4, 5, 6, 7, 8, 9], k = 5.
start = middle, middle = 5, and then I checked the code I studied, and I added one in the definition of middle.

 int middle = start + (end - start + 1)/ 2;

I want to make sure that if start = 5, end = 6, middle = 6 not 5, otherwise middle = 5, go to deadloop.

Binary search
I did come out the idea to use binary search, even I read lee215's post but I could not figure out. After I read the discussion post with the source code here, I quickly understood binary search is the solution.

public class Solution {
    public int MaximizeSweetness(int[] sweetness, int K) {
        K = K + 1; // Include yourself.
        
        int start = sweetness.Min();
        int end  = sweetness.Sum();
        
        while (start < end) {
            int middle = start + (end - start + 1)/ 2;
            
            if (split(sweetness, middle) < K) 
            {
                end = middle - 1;
            } 
            else 
            {
                start = middle; // include middle
            }
        }
        
        return start;
    }

    /// make sure every cut is at least minSweetness
    private int split(int[] arr, int minSweetness) {
        int peopleCount = 0;
        int sweetness = 0;
        
        foreach (int val in arr) {
            sweetness += val;
            
            if (sweetness >= minSweetness) {
                peopleCount++;
                sweetness = 0;
            }
        }
        
        return peopleCount;
    }    
}

Leetcode discuss: 308. Range Sum Query 2D - Mutable

 Here is the link. 

First practice - C# - copy and learn

Dec. 7, 2020
I will take some time later to figure out what is challenge for this algorithm. I like to practice first using prefix sum, and also every time Update API is called, row prefix sum is updated accordingly.

public class NumMatrix {

    private int[][] rowPrefix;
    private int[][] matrix;
    private int rows;
    private int cols;

    public NumMatrix(int[][] matrix) {
        this.matrix = matrix;
        this.rows = matrix.Length;
        this.cols = rows > 0 ? matrix[0].Length : 0;
        
        this.rowPrefix = new int[rows][];
        
        for (int row = 0; row < rows; row++)
        {
            rowPrefix[row] = new int[cols];
            
            updateRowPrefix(row, 0);
        }
    }      
    
    public void Update(int row, int col, int val) 
    {
        matrix[row][col] = val;
        updateRowPrefix(row, col);
    }      
    
    public int SumRegion(int row1, int col1, int row2, int col2) {
        var sum = 0;
        for (int row = row1; row <= row2; row++)
        {
            sum += getSumRow(row, col1, col2);
        }
        return sum;
    }
    
    /// support function 
    private int getSumRow(int row, int cFrom, int cTo)
    {
        var sumTo = rowPrefix[row][cTo];
        var sumFrom = cFrom > 0 ? rowPrefix[row][cFrom - 1] : 0;
        var answer = sumTo - sumFrom;
        return answer;
    }
    
    private void updateRowPrefix(int r, int c)
    {
        var sum = c > 0 ? rowPrefix[r][c - 1] : 0;
        
        for (int col = c; col < cols; col++)
        {
            sum += matrix[r][col];
            rowPrefix[r][col] = sum;
        }
    }
}

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix obj = new NumMatrix(matrix);
 * obj.Update(row,col,val);
 * int param_2 = obj.SumRegion(row1,col1,row2,col2);
 */

Leetcode discuss: 1293. Shortest Path in a Grid with Obstacles Elimination

 Here is the link. 

First practice - C# - BFS - obstacle as third dimension - Learning

Dec. 7, 2020
Introduction
It is challenge for me to train myself as a good thinker. I solved 590 algorithms, but I could not come out the idea using three dimension visited array to mark visit in BFS, obstracles can be used like third dimension. I still need to prove that the third dimension is working to prevent missing path in terms of getting minimum result. Right now I leave it as an assignment for the short future.

BFS approach
It is same as a classical BFS algorithm, and keep track of obstracles removed for current state.

public class Solution {
    /// <summary>
        /// Dec. 7, 2020
        /// study code
        /// https://leetcode.com/problems/shortest-path-in-a-grid-with-obstacles-elimination/discuss/558572/Accepted-C-BFS-Solution
        /// </summary>
        /// <param name="grid"></param>
        /// <param name="k"></param>
        /// <returns></returns>
        public int ShortestPath(int[][] grid, int k)
        {
            int rows = grid.Length;
            int columns = grid[0].Length;

            // it is tough for me to figure out using three dimension - third dimension -
            // k obstacles to remove
            var visited = new bool[rows,columns,k + 1];

            var queue = new Queue<int[]>();
            queue.Enqueue(new int[]{0,0,k});

            visited[0, 0, k] = true;

            var directions = new int[4][];
            directions[0] = new int[]{-1, 0};  // top
            directions[1] = new int[]{0, 1}; // right
            directions[2] = new int[]{1, 0}; // down
            directions[3] = new int[]{0, -1}; // left
            
            int minimum = 0;
            while (queue.Count > 0)
            {
                int count = queue.Count;
                for (int i = 0; i < count; i++)
                {
                    var current = queue.Dequeue();
                    var row = current[0]; 
                    var col = current[1]; 
                    var left = current[2];

                    if (row == rows - 1 && col == columns - 1)
                    {
                        return minimum;
                    }                   
                    
                    foreach (var dir in directions)
                    {
                        var newRow = row + dir[0];
                        var newCol = col + dir[1];

                        if(newRow < 0 || newRow >= rows || newCol < 0 || newCol >= columns)
                        {
                            continue;
                        }

                        if (grid[newRow][newCol] == 0 && !visited[newRow, newCol, left])
                        {
                            // keep the same obstacles - but mark visit for left number
                            visited[newRow, newCol, left] = true;
                            queue.Enqueue(new int[]{newRow, newCol, left});
                            continue;
                        }

                        if (grid[newRow][newCol] == 1 && left != 0 && !visited[newRow, newCol, left - 1])
                        {
                            // replace current obstacle with 0, decrement variable left
                            visited[newRow, newCol, left - 1] = true;
                            queue.Enqueue(new int[]{newRow, newCol, left - 1});
                        }
                    }
                }

                minimum++;
            }

            return -1;
        }
}

Monday, December 7, 2020

30 hard level algorithms: google - Leetcode premium

 Dec. 7, 2020

Introduction

It is a tough project to work on. I am planning to go over the following 30 hard level algorithms provided by Leetcode premium. 

30 hard level algorithms

I like to go over those 30 hard level algorithms

  1. 727 Minimum windows subsequence - DP, sliding window
  2. 715 Range Module, Segment tree, ordered map
  3. 552 Student attendance record II, DP
  4. 465 Optimal account balancing 
  5. 1499 Max value of equation, Array, sliding window
  6. 753 Cracking the safe, math, DFS
  7. 1231 Divide Chocolate, Binary search, greedy
  8. 308 Range sum query 2D - Mutable, Binary indexed tree, segment tree
  9. 1293 Shortest path in a grid with obstacles elimination
  10. 1444 Number of ways of cutting a pizza, DP
  11. 527 Word abbreviation, string, Sort
  12. 1240 Tiling a rectangle with the fewest squares, DP, backtracking
  13. 315 Count of smaller numbers after self, Binary search, Divide and conquer, Sort, binary indexed tree, segment tree
  14. 460 LFU cache, design
  15. 1406 Stone game III
  16. 248 Strobogrammatic Number III
  17. 1610 Maximum number of visible points, Two pointers, Geometry
  18. 335 Self crossing, Math
  19. 420 Strong password checker
  20. 1345 Jump Game IV
  21. 679 24 Game
  22. 642 Design search autocomplete system
  23. 1377 Frog position after T seconds 
  24. 818 Race car
  25. 1255 Maximum score words formed by letters, bit manipulation
  26. 174 Dungeon game, binary search, DP
  27. 1125 Smallest sufficient team, DP, bit manipulation
  28. 68 Text justification, string
  29. 847 shortest path visiting all nodes, DP, BFS
  30. 732 My calendar III, segment tree, ordered map

Actionable Items

I only studied and learned the top nine hard level algorithms, still I have 21 algorithms to review. Now it is 11:34 PM. 

  1. 727 Minimum windows subsequence - DP, sliding window
  2. 715 Range Module, Segment tree, ordered map
  3. 552 Student attendance record II, DP
  4. 465 Optimal account balancing 
  5. 1499 Max value of equation, Array, sliding window
  6. 753 Cracking the safe, math, DFS
  7. 1231 Divide Chocolate, Binary search, greedy
  8. 308 Range sum query 2D - Mutable, Binary indexed tree, segment tree
  9. 1293 Shortest path in a grid with obstacles elimination
Life is tough. I should plan early and have more time for those 21 hard level algorithms. 

Sunday, December 6, 2020

Monotonic increasing - stack

 骨骼面试有一类比较Tricky的题目,即单调队列 Monotonic Queue / 单调栈 Monotonic Stack

通过单调队列,可以把复杂度降到O(n),主要覆盖Stack, Sliding Window, Two Pointers, Linked List/Queue, HashTable/HashMap等算法和数据结构,分类参考主贴

https://www.1point3acres.com/bbs/thread-649468-1-1.html

6个月内
LC42. Trapping Rain Water
LC84. Largest Rectangle in Histogram (单调递增栈)
LC122. Best Time to Buy and Sell Stock II   
LC239. Sliding Window Maximum (有界最大值单调递减栈)
LC496. Next Greater Element I
LC862. Shortest Subarray with Sum at Least K (单调递增栈)
LC1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit

LC907. Sum of Subarray Minimums, 
LC1019. Next Greater Node In Linked List,