Showing posts with label hard level algorithm. Show all posts
Showing posts with label hard level algorithm. Show all posts

Tuesday, June 23, 2020

Leetcode discuss: 301 remove invalid parentheses

Here is the link.

C# BFS approach without pruning practice in June 2020

June 23, 2020
301 remove minimum invalid parentheses
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.
How to study a solution written in BFS?
I did learn the idea back in 2018. After two years, I learned and value the case study method.
It is important for me to write down a few simple test cases first, and then go over one by one to explain to myself first, how to apply BFS and make it work.
Case study approach from Harvard business school teaching is my favorite learning technique now.
I like to study the BFS solution again written in Java. The link is here.
2020 June 23 - review BFS solution
I plan to write a C# solution using the same idea. I will write a case study to explain how to approach the problem.
I spent a few hours to write a solution on June 22, 2020, here is the link.
I debugged the code using string "())" step by step, it is hard for me to understand the design. So I decide to remove pruning part, focus on basic BFS algorithm. Keep minimum to solve the problem first using BFS.
I decided to continue to remove those code hard to understand, in other words, make it work for Leetcode online judge, remove those pruning ideas first.
Case study
I think that it is important to go over test cases and then talk about how to solve the problem. The following is to go over valid string, remove one parenthese, two parentheses three test cases.
Case study 1: "()", or any valid string
The string is valid, so first step in my design is to check original string is valid or not.
If it is, then return original string. Othewise at least one parenthese should be removed.
Case study 2: Remove one parentheses
For example "())", minimum number of invalid parentheses is 1, result is "()".
"(()"
How to design using BFS algorithm, using Queue to help.
Remove one char from index = 0 to last one, and see if the substring is valid or not.
index = 0, "))", not valid
index = 1, "()", valid
index = 2, "()", valid
The way to skip the duplicate valid string is to save in the hashset first, and then convert set to List object.
Rule 1:
Always consider to put "))" in the queue to process since original one "())" is not a valid string.
One more test to remove one char - case study 2
")()"
First, it is not valid parentheses string. So at least one parenthese should be removed.
Go over each index position of string ")()", remove the char,
First one is index = 0; "()" is valid one, so "()" is added to output;
Second one is index = 1; "))" is not valid, but output list is not empty, continue;
Third one is index = 2; ")(" is not valid one.
It is important to go over a few simple test cases to understand how BFS works, and then it is time to think about how to prune the algorithm and make it more efficient.
Here are those two important steps:
First, push Tuple<string, int>("())",0) into queue;
and then go over each index position from "())", remove each char, see if it is valid or not.
Case study 3: Remove two parentheses
String "())(()", how to find the valid string "()()" to remove two parentheses?
The above test case is so helpful for me to understand BFS, first remove one parenthese, all those substrings are not valid string, which should be queued for next removal of one more parenthese.
Work on the iteration. I will add more explanation for this test case.
"))(()", 0
"()(()", 1
"()(()", 2
"())()", 3
"())()", 4
"())((", 5
From the above four test cases, "())()", 4 should be same as "())()", 3. So it is not needed to be repeated.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _301_remove_invalid_parentheses___2020
{
    class Program
    {
        static void Main(string[] args)
        {
            //var reuslt  = RemoveInvalidParentheses("()"); // original one valid or not - step 1
            //var result2 = RemoveInvalidParentheses("())"); // need to remove one parentheses - step 2
            var result3 = RemoveInvalidParentheses("())(()");
        }

        /// <summary>
        /// code review on June 22, 2020
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static IList<string> RemoveInvalidParentheses(string s)
        {
            var set = new HashSet<string>(); 

            // return if it is valid
            if (IsValidParentheses(s))
            {
                set.Add(s);
                return set.ToList();
            }

            // How to design a queue - BFS to solve the problem? 
            // think about a string to remove two parentheses, "())(()", 
            // those strings with one char removed should be enqueued for next removal of char
            var queue = new Queue<Tuple<string, int>>();

            //The 2-Tuple is (string, startIndex)                        
            queue.Enqueue(new Tuple<string, int>(s, 0));            

            while (queue.Count > 0)
            {
                var current = queue.Dequeue();

                var search = current.Item1;
                var start = current.Item2;               

                // Start from last removal position 
                for (int index = start; index < search.Length; ++index)
                {
                    var visit = search[index];

                    //Not parentheses
                    bool isParenthese = visit == '(' || visit == ')';
                    if (!isParenthese)
                    {
                        continue;
                    }                                        

                    // remove visit char from the string
                    //var skipCurrent = search.Substring(0, index) + search.Substring(index + 1);
     var skipCurrent = search.Remove(index, 1);
     
                    //Check the string is valid
                    if (IsValidParentheses(skipCurrent))
                    {
                        set.Add(skipCurrent);
                        continue; // continue to iterate all index positions 
                    }

                    // think about test case: ())(() -> minimum is to remove two parentheses
                    // Strings with one char removed are not working, then BFS goes to second remove
                    // Four strings will be in the queue. 
                    // "))(()", 0
                    // "()(()", 1
                    // "()(()", 2
                    // "())()", 3
                    // "())()", 4 
                    // "())((", 5    
                    if (set.Count == 0)
                    {
                        queue.Enqueue(new Tuple<string, int>(skipCurrent, index));
                    }
                }
            }

            return set.ToList();
        }

        /// <summary>
        /// test case: 
        /// "()","(())", "()()"  true
        /// ")(","())"  false
        /// Time complexity: O(N)
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsValidParentheses(String s)
        {
            int count = 0;
            for (int i = 0; i < s.Length; ++i)
            {
                var visit = s[i];
                var isOpen = visit == '(';
                var isClose = visit == ')';

                if (isOpen)
                {
                    ++count;
                }

                if (isClose)
                {
                    bool noOpenToMatch = count <= 0;

                    if (noOpenToMatch)
                    {
                        return false;
                    }

                    count--;
                }
            }

            return count == 0;
        }
    }
}


Wednesday, October 9, 2019

How to write an excellent post?

Oct. 9, 2019


Introduction


It is the first time I learn that I can write a post and then get 4 upvotes, ranking top 7 in one of hard level algorithm on Leetcode.com. 


Crafting skills


I think that it is not difficult to write an excellent post. The algorithm I studied is so easy to understand, since I put together some comment to make it so straightforward.

And also I did spend time to save a graph from weekly contest lead board, and then wrote down my observation.

It is so important for me to learn how to document good learning process for other players.

Being a good mentor, helper, or player, I strive to document my practice, my learning, and then I have chance to meet more people in the world. I do believe that it takes so much time for me to learn and master one algorithm. I also certainly like to see people advance skills quickly, since they learn from my experience through my post or blog. I always like to be one of players, share and learn, learn and then share.

Here is the link.



Thursday, May 3, 2018

Leetcode 269: Alien dictionary

May 3, 2018

Introduction



Plan to spend time to review the algorithm again. I like to make the algorithm my favorite graph algorithm in May, 2018.

I know that it takes a lot of sacrifice from a friend who coached me through mock interview, how to pay attention to detail. I like to write the blog to express my thankfulness to those people. Like my hitting partner on tennis court, those algorithm practice trains me to think harder, work harder.

One day I wish I can use my mathematics training over years and be able to use some of them in the work. First I have to learn how to be a good software programmer first, my crafting skills has to be improved first.


Learn how to talk in Chinese about the algorithm


It is interesting to learn how to express the algorithm in Chinese first. I did 10 minutes study and made a gist based one a blog. Here is the gist. The algorithm with some improvement is here.

Plan to write a C# solution based on the above Java code.

Past practice


Here is my past practice. I need to warm up and write a solution. It is such great warmup to review code written more than 2 years ago, here is code I reviewed and wrote this time.


Thursday, April 19, 2018

Leetcode 126: Word ladder II

April 19, 2018

Introduction


It is the hard level algorithm and I like to spend time to go over a few more ideas through Google search and Leetcode discussion panel. What I like to do is to go over more detail how to address time and memory limit exceeded problem.

I spent over 30 minutes already to go over my past practice and a few ideas, I like to go over this blog.

Algorithm practice


I like to go over the notes written in Chinese in the blog, and then rewrite some of them, make it my own.

As a programmer, most of important for me right now is to be a good thinker. I like to search the blogs and find the idea to help me think clearly. Here is the notes:

LeetCode中为数不多的考图的难题。尽管题目看上去像字符串匹配题,但从“shortest transformation sequence from start to end”还是能透露出一点图论中最短路径题的味道。如何转化?

1. 将每个单词看成图的一个节点。
2. 当单词s1 改变一个字符可以变成存在于字典的单词 s2 时,则s1与s2之间有连接。
3. 给定s1和s2,问题I转化成了求在图中从s1->s2的最短路径长度。而问题II转化为了求所有s1->s2的最短路径。


How do I go over the notes above? 

1. Google search the shortest path in the graph. 
2. How to define a graph? Every word is a node in the graph. 
3. Node s1 and node s2 have a connection -> how to define it?

Let me work on the notes in the following:

无论是求最短路径长度还是求所有最短路径,都是用BFS。在BFS中有三个关键步骤需要实现:

1. 如何找到与当前节点相邻的所有节点。
这里可以有两个策略:
(1) 遍历整个字典,将其中每个单词与当前单词比较,判断是否只差一个字符。复杂度为:n*w,n为字典中的单词数量,w为单词长度。
(2) 遍历当前单词的每个字符x,将其改变成a~z中除x外的任意一个,形成一个新的单词,在字典中判断是否存在。复杂度为:26*w,w为单词长度。
这里可以和面试官讨论两种策略的取舍。对于通常的英语单词来说,长度大多小于100,而字典中的单词数则往往是成千上万,所以策略2相对较优。

2. 如何标记一个节点已经被访问过,以避免重复访问。
可以将访问过的单词从字典中删除。

3. 一旦BFS找到目标单词,如何backtracking找回路径?



Tuesday, April 10, 2018

Being an interviewer: Leetcode 84: Largest rectangle in histogram

April 10, 2018

Introduction


It is my favorite algorithm and it is hard level algorithm in Leetcode.com. I chose this algorithm to interview a friend met on mock interview. He is preparing next week Google onsite, and I gave him another hour mock interview.

I practiced the algorithm recently, but I found out that I still missed something important. The optimal time complexity is O(n), and it has to use stack to save the index of rectangle left boundary when ascending. The stack is always keeps non-descending heights. Here is the link for my past practices.

Learning is fun


The peer is very organized and also very good at explaining the algorithm. I specially like the way he structured the content.

Here is the script for the mock interview.


Friday, February 2, 2018

Leetcode 42: Trapping Rain Water

Feb. 2, 2018

Plan to work on hard level algorithm Leetcode 42.

May 23, 2018

I could not believe that I could not come out the idea to calculate the rain water when I was asked by the interviewer on May 22, 2018 8:30 PM. I asked one of Chinese graduate student to help me, give me some mock interviews and this was the first algorithm he asked me.

I talked about descending stack, and the interviewer asked me why it is the descending stack. And also the interviewer asked me to give out the correct brute force solution. I noticed that my brute force solution is also not correct. I need to find left boundary for the current bar which should be maximum height of prefix elements.

Here is my C# solution written after the mock interview.

There are three issues to fix in order to pass online judge. First one is to check base condition, check array length is 0 on line 26; second one is to apply Array.Reverse API, it has been called three times. And the last one is to add if condition statement on line 49.

Thursday, January 18, 2018

Leetcode 84: Largest rectangle in histogram

January 18, 2018


Introduction


It is the hard level algorithm and it can be solved used stack to achieve the optimal time complexity O(N) where N is the array's length. The algorithm is called largest rectangle in histogram.

On January 17, 2018, I had a mock interview and I was asked to work on the algorithm. I went through the brute force solution first, but I did not come out the optimal solution to lower the time complexity to O(N).

One more practice 


What I like to do is to study one blog I like in June 2015, and then rewrite the notes and also write the C# code as well. It is very easy to look up past practice, here is my blog to document the practice in June 2015. At that time, I was shy and did not write down my thinking process to learn to solve the problem.

Right now, I think that it is very important to write down thinking process and also the idea to break through the hurdles in the problem solving. I think that it is more important to build up some new habit to learn to solve a problem. Write down the constraints, write down the problem, difficult issues, concerns, and then ideas to solve the problem, or ideas to solve partial solution. 

First, I rewrote the note to make it more readable, and then saved a gist. Here is the gist. 


Analysis of the algorithm



Most of important is to write down the analysis, and that is something lasting longer than coding itself. Specially if I draw something to highlight the main design ideas and it will be extremely helpful to bring back the memory.

This time I drew one to help myself learn the design using stack, check upward and downward.


The main idea is very simple to explain in Chinese, let us take a look at notes in Chinese first, and then I quickly explain them in English.


Going upward



When the graph is going upward, in detail, current index is i, next step is i + 1, and height[i] < height[i + 1], there is no need to calculate the area. Since it is getting bigger value when i moves to next value.

Going downward



When the graph is going downward, in detail, current index is i, next step is i + 1, and height[i] > height[i + 1]. it is time to calculate the current rectangle's area.

Get help from a stack 


At the current index i, only right end's index is known, how to get the left end's index? So in order to iterate the array, a stack is need to maintain the backtracking history.

How to design a stack?


In this stack the right end's index is saved to the stack, but when is time to push into stack? Every time there is element in the array which is bigger than the top of the stack, push the index to the stack. Otherwise it is time to calculate the current rectangle's area and compare to the largest area.

Every algorithm will become one of your valuable weapons 
until you teach some one and show him/ her how it work.