Showing posts with label Leetcode 301: remove invalid parentheses. Show all posts
Showing posts with label Leetcode 301: remove invalid parentheses. 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;
        }
    }
}


Tuesday, February 20, 2018

Train insane or remain the same

Feb. 20, 2018

Introduction


I always like to answer people why I continuously practice mock interview. I believe that I treat it like a research project. I like to find out what I can learn quickly through those practice.

I love to meet people and then have discussion related to the algorithm and data structure. I just treat the mock interview as one of my research projects, things I can work hard and then see if I can make a difference on myself or the peer. Learning and teaching are most interesting things I like to do.

I love to teach algorithm and data structure, I go through those long hours to work with peers on algorithm problem solving. From those experience, I educate myself to be super patient when I meet an algorithm and I need to break through in less than 20 minutes.


My two mock interviews


One thing I did last weekend is to go through the mock interviews from a startup company, and so surprisingly I passed two round of mock interviews, first one is average 2.7, second one is average 2.5.

Life is such a great teacher. One of my friends told me that he did first one 3.2 but second one did not pass passing bar 2.5, which is below 2.0.

So I suddenly find out that I should think about it. Do not take it for granted. At least I should write down and then later on I can tell something from this experience.


Leetcode 301 Algorithm practice


I like to figure out how to improve my practice. Just before the mock interview to work on remove invalid parentheses, I worked on leetcode 301 hard level algorithm continuously from February 3 to February 7 2018, and my goal was to practice over 10 times and also tried various ideas. Here is the list of practice by searching the blog using Leetcode 301.

One thing I like to look into is that I could not recall the tip to remove invalid parentheses. The most simple way to remove invalid parentheses is to remove unmatched close bracket first, and then reverse the string, and then handle the same subproblem.

What I can tell is that the practice does not help right away for the mock interview. I got the average score 2.5, but score of coding is 2.3. I did know that I just practiced the algorithm and got my hands on those ideas one by one, but I could not quickly enough to apply related work to the problem solving right away. The interviewer gave me 5 minutes talk after mock interview, he told me that the first algorithm took me around 30 minutes which should less than 20 - 25 minutes.

What if I did not review the algorithm, and went over all the practices I did over 6 hours.


Saturday, February 17, 2018

Remove invalid parentheses

Feb. 17, 2018

Introduction


It is my most favorite algorithm to work on recently. I started to work on Leetcode 301 - remove invalid parentheses, a hard level algorithm

Time line


Step 1:

I had 9:30 AM mock interview, and the algorithm I had to work on is to find minimum valid parentheses. Based on the interviewer's advise, I had to work on step 1, remove invalid parentheses.

It should take me at most 20 to 25 minutes to finish the algorithm and also coding, but I actually spent over 35 minutes but the code has bugs.

Here is the code in mock interview

Step 2:

Started from 1 PM, I worked on the code I wrote in mock interview, completed the code, and the code has bug. Here is the code.

Step 3:

2:47 pm, I rewrote the code using different design, and complete the test cases. The code is here for remove invalid parentheses.

Step 4:

Not it is 2:55 PM, I organized all the past practice Leetcode 301, added tag for each post using tag: Leetcode 301: remove invalid parentheses. All the past practice can be looked up by the tag now.


Actionable Items


March 5, 2018 8:10 PM
It has been more than three weeks, I think about carefully what really the problem is for this algorithm problem solving.

I need to simplify the problem I need to solve.

Wednesday, February 7, 2018

Leetcode 301: Breadth first search and pruning (VIII)

Feb. 7, 2018

Introduction


It is interesting to read the gist I prepared for the discussion of breadth first search and how to prune the algorithm. Here is the link. And here is the C# code.


Leetcode 301 - How to speed up the algorithm? (VII)

Feb. 7, 2018

Introduction

I spent time to read the discussion how to speed up search. Here is the gist.


Leetcode 301: Remove invalid parentheses (VI)

Feb. 7, 2018

Introduction

It is interesting to read and study the solution based on breadth first search. Here is the gist I prepared for my study.

It is time for me to warmup the skills using Queue to solve an algorithm. Here is the C# code.


Leetcode 301: remove invalid parentheses (V)

Feb. 7, 2018

It is such a good workout that I wrote a C# code based on one of solutions in Leetcode discussion. Here is my C# solution.


Leetcode 301: remove invalid parentheses (IV)

Feb. 7, 2018

This is one of solutions I like to study. First I prepare the study note for myself. Here is the link.

Leetcode 301: remove invalid parentheses (III)

Feb. 7, 2018

I like to review the study note later for this algorithm. It is written in Chinese. Here is my gist file.


Saturday, February 3, 2018

Leetcode 301: Remove invalid parentheses (I)

Feb. 3, 2017

Introduction


I like to work on a hard level algorithm on leetcode.com. Also I do not like to rush and find the answer as I do sometimes. Through the learning experience of Leetcode 10: regular expression matching, I know that it may take me 10 practice before I can fully understand the hard level algorithm.


First 30 minutes


On Feb. 6, 2018 I had chance to work on the algorithm called Leetcode 301: removed invalid parentheses. I like to write down the notes and see if I can find the answer through the first 30 minutes work.





Monday, July 31, 2017

Leetcode 301: Remove Invalid Parentheses

July 31, 2017

Plan to work on the hard level algorithm called "Remove Invalid Parentheses". Spent over 2 hours to study the leetcode discussion, and figured out what to work on. The most important is to write code using other people's ideas, Julia found one person to summarize the solutions  through the discussion. Julia likes to apply Terse, Expressive, and Do one thing  (TED) principle she learned through pluralsight.com course clean code. She likes to go over the handout of the course and apply some notes to her practice. The handout link is here.

Three code principles are outlined in the article called "3 Core Principles to Write Clean Code".

Depth first search I


1. Plan to study Java solution provided by a Google engineer first. Here is the link.

Spent over 2 hours to study the code, Julia wrote one with three test cases. Here is her C# code. Use readable function name, C# code is here.

Depth first search II



2. Plan to study Java solution provided by dietpepsi. Here is the link.

Spent over 2 hours to study the code, Julia wrote one with three test case. Here is her C# code.

The design idea is to scan the string twice, first scan is to remove the invalid ')'; then reverse the string with removed invalid ')', in other words, scan right to left virtually; this time is to remove invalid '('.

After removing invalid ')' and '(', reverse the search and then add to valid strings list.

Breadth first search ( no pruning )


Plan to study the discussion, the link is here

C# practice code is here

Breadth first search ( pruning )




Plan to study the BFS solution written in Java. The link is here.

A few ideas are applied to prune the BFS algorithm.

Julia's C# practice is here.

4. Plan to read this review of all solutions. The discussion link is here.

5. Plan to study the analysis written in Chinese:

对于一个字符串,在任何时候如果 ')' 的个数多于左括号,则说明从开始到现在位置必然可以删除一个')'.而这段子串可能包含多个')',删除哪一个呢?当然删除任何一个都可以.

例如对于()())(),从开头到 s[4] 位置构成的子串多了一个右括号,因此我们需要删掉一个,而这个子串有三个右括号,但是只会产生2个结果,也就是会有一个重复值.所以在删除括号的时候,为保证不会产生重复值,需要记录一个最后删除的位置,这样可以使得在接下来删除的时候只删除这个位置之后的值.这样我们可以使得当前这一段子串不再包含多余的右括号了.这样我们可以删除了一个右括号之后合法的子串与后面还没有检查过的子串组成一个新的字符串重新开始检查.直到不再含有非法的右括号.

但是还有一种情况是包含了多余的左括号,一种直观的方法是从右向左再按照上面的方法处理一遍左括号.但是将数组逆置之后就可以重用上面的算法了.

所以总的思路就是先对字符串进行处理使得其不再含有非法右括号,然后将其翻转以后再检查是否含有非法的左括号.最后左右括号都检查完之后都合法就是我们要的答案了.

时间复杂度应该是O(n^2).

Plan to study C++ source code. Here is the link. The blog link is here