Showing posts with label sliding window. Show all posts
Showing posts with label sliding window. Show all posts

Wednesday, July 17, 2019

76. Minimum Window Substring

Here is my discussion post.

It is challenging task to design a sliding window which has intelligent way to keep count all chars in sliding window no matter whether the char is in pattern string t. I choose to write a solution based on the video shared by Daniel Su.
Small case stduy
S = "ACBA", t ="AB",
There is step by step explanation in the above video using the above test case. I also like to explain the design.
About right pointer in sliding window
if the char on position of right pointer has bank value bigger than 0, then the char is counted towards to be one of chars in pattern t. Variable count decrement one, no matter right char is in pattern string t, bank count will always decrement one.
About left pointer in sliding window
It is similar idea to hanlde left pointer. The argument is if bank[leftChar] > 0, then one of chars in pattern string s is removed from sliding window, count variable should decrement one.
Why it is hard level algorithm?
  1. Fact 1:
work on test case
s = "ABBB", t = "ABB", the pattern string may have duplicate chars. So the sliding window should contain all unique chars and also its count for each char. The minimum sliding window should contain all unique chars in pattern string, and also keep at least same count for each char as well.
  1. Fact 2:
    How to determine if the string in sliding window contains (denoted as sw) all chars in pattern string s?
counting sort all chars in sw and s, and then compare each char and its count. This takes O(k) time, k is distinct chars in pattern string t. It can be O(1) time instead.
How to design the technique to make it O(1)?
For example, S = "ACBA", t ="AB".
Char C's bank value from 0 to -1, and then left pointer moves away index = 1, go back to 0. Since C is not in string t, C's bank value will never go beyond 0.
First it is the design in template to document all chars in sliding window using bank array. Even the characters not in pattern string t will be recorded, for characters in pattern string t will be recorded using bank array, since pattern string may have more than one copy of the same char, how to tell which copy of char goes to count of pattern string t.
Next count variable is introduce to keep "the sliding window contains all char and it's count in pattern string t" checking O(1) time.
Five minutes to understand count variable design
It is tough job to design count variable. It took me hours to understand when to increment one to count variable, when to decrement one to count variable.
One thing I can do is to show a simple test case. The solution is not difficult to write at all.
s = "AAB", t = "AB",
I think that the above test case first 'A' is visited, bank['A'] = 1, so count variable should be incremented by one. Second 'A' is visited, bank['A'] = 0, so count variable will not be incremented.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _76_minimum_window_substring_optimal
{
    class Program
    {
        static void Main(string[] args)
        {
        }

        /// <summary>
        /// July 15, 2019
        /// study code
        /// https://www.youtube.com/watch?v=9odu9ImG9oY
        /// </summary>
        /// <param name="s"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public string MinWindow(string s, string t)
        {
            if (s == null || s.Length == 0 ||
                t == null || t.Length == 0)
            {
                return "";
            }

            var bank = new int[256];
            
            int left = 0;
            int right = 0;
            int count = 0;

            int min = Int32.MaxValue;
            string minString = "";

            var pLength = t.Length;
            var length = s.Length;

            for (int i = 0; i < pLength; i++)
            {
                bank[t[i]]++;
            }

            while (right < length)
            {   
                var rightChar = s[right];
                right++; // always advance one to next iteration 
                if (bank[rightChar] > 0)
                {
                    count++;
                }

                bank[rightChar]--; // always decrement one no matter char is in pattern t or not                              

                // move left pointer until missing one char from string t
                while (count == pLength)
                {
                    var size = right - left;
                    if (min > size)
                    {
                        min = size;
                        minString = s.Substring(left, right - left);
                    }

                    // shift our window
                    var leftChar = s[left];
                    left++;  // always move left pointer
                    bank[leftChar]++; // always increment one no matter char is in pattern t or not                    

                    if (bank[leftChar] > 0) // that means left char is one of chars in pattern, also count as one
                    {
                        count--;
                    }
                }
            }

            return minString;
        }
    }
}


Saturday, January 6, 2018

Find smallest substring containing keys

January 6, 2017

Introduction


It is another 8:00 PM interview and I had to work on the algorithm called "Find smallest substring containing keys". I have worked on the algorithm over five times last nine month, on my last mock interview the peer helped me and advised me to change my idea to slide left pointer of sliding window.

Here are blogs related to search results using keyword: smallest substring in my coding blog.

Here are last few practices:

Oct 28, 2017 practice is here.

Dec. 2, 2017 practice is here.

Code review 


The peer helped me through the whole process, I spent over 38 minutes to write code, pass all test cases except one. Here is the code.

Follow up

Now it is 10:50 PM, I used Visual Studio to debug the code and found the bug. On line 52, left < i should left <= i since one char should not excluded as a substring. The start and end position are the same index value.

I do not need to use HashSet to check if the char is one of keys, seen line 17, I can just use dictionary.ContainsKey(visit) to find out, seen line 30. I looked up my last practice and it is the advice from my peer back in Dec. 2017.

Feedback from the peer


It is very hard algorithm for me to work on in mock interview, but with the peer's help, I managed to find bugs early and continued to write and completed the code.

Here is the feedback.


Editorial notes


Programming is such a fun activity for me now. At least today January 6, 2018 I had so much fun to work with best talent programmers in the world.

Every mock interview is so surprising. The first one I was surprised to work with a peer, he taught me how to write a spiral matrix print, and then we exchanged the experience about algorithm problem solving related to same tree and median of stream. The second one was more surprising, because this one told me that he will work on Google onsite interview next month. The third one was even more surprising, I was busy learning python to follow the peer's problem solving, and then peer told me that she is M.I.T. computer science graduate.

How difficult is it to work on algorithm and data structure problem solving? I have worked on the hard level algorithm to find smallest substring so many times, and then I finally understood that how long it takes me to master an algorithm. It takes me 3 years to fully master the algorithm.

Today every step the peer worked me through the code and asked me questions, meanwhile I explained to the peer what I tried to work on, I even copied the analysis and then said that I do not know what to do, just make sure the test case will work for first three chars, and then go over each char through whiteboard testing.

How patient I will be when I write code in daily work, can I write production ready code every day starting from January 8, 2018? Do I need to write hard level code like this one "Find smallest substring containing keys" every day?

The mock interview experience just brought the whole world to me. I sit in the home office whole day and continuously work on the algorithm, exchange the tips to solve the problem.

I know that programmer life should be easy once I master the skill to work with smart people, open and share the ideas.

Saturday, March 5, 2016

HackerRank: Bear Steady Gene (II)

March 4, 2016

  Problem statement:

https://www.hackerrank.com/contests/hourrank-6/challenges/bear-and-steady-gene  

A gene is represented as a string of length  (where  is divisible by ), composed of the letters , and . It is considered to be steady if each of the four letters occurs exactly  times. For example,  and are both steady genes.


Julia's 1st practice:

https://gist.github.com/jianminchen/80723bae951328a690bb

score 15 out of 50, there are 2 run time error, failed a few of test cases.

The algorithm ends up in time complexity O(n^2), close to brute force solution - O(n^2)

C# code implementation to study:

Readable code, with some analysis.

https://gist.github.com/jianminchen/fae9142eff9a6f9643fc

Work on two pointers, sliding window, so time complexity is O(2n) = O(n).

Let us go over two pointers algorithm here using example:
GAAATAAA,
A - count of A, denoted as cA = 6, n/4 = 2, so we have to change 4 of A to other thing.
A - 6 -2  = 4 , 4 of change
C - 0 - 2 = -2
T - 1 -2  = -1
G - 1-2  = -1
So, at least minimum is 4, but a substring containing 4 of A, shortest one is AAAA. But "AAAA" is not a substring of "GAAATAAA"

G  A A A T A A A
0
start
end
Let us find the substring starting from 0, but will include all 4 of A, and then, rest of string will not include any char of "ACGT" more than n/4.
GAAATA, string length is 6.
start - 0, index of 0
end - A, index of 5

continue to move start to next one, 1, then, G is moved out from substring, adjust count of chars.
AAATA can be the substring, so the length is min (6, 5) = 5. Do not need to move end pointer.

next step, start = 2, missing one of A, and then, end has to move to next one until the string fits into requirement.

Every thing should be covered in 20 minutes, problem reading, the design of algorithm, the coding.

So, Julia had second practice, and the code scores 50 out of 50 this time.

https://gist.github.com/jianminchen/61dfe437f82edb9793fc

Conclusion:
After coding, Julia understands the algorithm much better.

1. count of array for substring, cB=[4, -2, -1, -1], so in other words, substring has to contain at least 4 'A', C, G, T does not matter.

So, using CB array, sliding windows of substring GAAATA, each time, end index is moving forward, tracking the substring's char by taking off from CB; in other words, GAAATA, CB will be
[0, -2, -2, -2].

Now, it is easy to understand that GAAATA will be added to the solution set, the length is 6.
line 51, if (cBAllLessThan1(cB)) 
<-add substring to solution set, compare length<- easy to understand now.

2. the code in https://gist.github.com/jianminchen/fae9142eff9a6f9643fc has discussion:
switch(line[0]) {
case 'A': A--; break;
case 'C': C--; break;
case 'G': G--; break;
case 'T': T--; break;
}
Julia removed those detail discussion, but her code takes more time. Because she created a bug in her writing. Debugging takes time.

3. Julia's practice in 2015, sliding windows, two pointers:
http://juliachencoding.blogspot.ca/search/label/slide%20window

April 27, 2016
change C# program to make variables more meaningful, matching the design:
code -> GENES <- global string array
function name matches design of two pointers moving.

https://gist.github.com/jianminchen/b8263048c297473319c23836e9468c14

searchStrArray -> searchStrNumber, more meaningful.
https://gist.github.com/jianminchen/b23c4f606a101b9aeec71eff3268db32

Tuesday, February 2, 2016

quicksort: a practice makes difference

February 2, 2016

Introduction


Julia likes to work on basic things on algorithm problem solving, like brute force solution, recursive function design, divide and conquer solution, partitioning; So, she can write code everyday on algorithm.

Review the quicksort algorithm, and then, practice using C# programming language. So many times Julia went through the review of quick sort algorithm, but to write it in less than 10 minutes, without a bug, without stress, takes more dedicated effort - maybe write a blog to share will help.   

Julia likes to rewrite the above paragraph in April 9, 2018.

Quicksort warmup


First review the blog - the quick algorithm written in Java programming language. Julia must like the idea of choosing pivot value - "// simple version - pick the right most value as the pivot ". As a sports programmer, Julia knows that the power of using a simple test case, she likes to do it here. 


Let us describe how quick sort works: - explain it using 5 minutes - 10 minutes to write the code 
1. First, using partition, and then, divide and conquer, use recursive function calls to solve the problem.
Most important technique, is to design a partition - 2-way partition, how to design the partition, choose pivot point, and how to do partition step by step.

Let us work on a simple example and have discussion, show the procedure of partition.
Let us say that array 1, 2, 3, 4, 5, 6, sorted, and then we like to derive the above sorted array using this one, 1, 3, 2, 5, 4, 6.

One of ideas to choose pivot position, is to choose last number in the array.  For the example, choose last one in the array, 6, the position of pivot point, left side <= 6, right side > 6. So, do you see the problem, 6 is not ideal case. No swap, second partition with 0 values; in other words, all values go to first partition. 

Two way partition


Let us work on another order: 1, 6, 2, 5, 4, 3

1. Choose pivot value - last element in the array, value 3
2. Work on partition the array, left partition <= 3, right partition > 3
3. Two partitions are 1, 2, 6, 5, 4, 3
4. Put the pivot point in-between, 1, 2, 3,  6, 5, 4
5. Divide and conquer, solve two small subproblems, use recursive call. 

Use a diagram to show progress:      
Scan the array once from left to right, and then keep track of the start position of right partition. Anything less than 3 will be in left partition.  

The easy way to remember is to find right partition's start position. 

Two way partition - test case

1, 2, 6, 5, 4, 3  -> move pivot value 3 between two partitions
1, 2, 3,  6, 5, 4

Step by step, 
first, after partitioning, the array is the following:
1, 2, 6, 5, 4, 3 
so right side partition starting from array's index value 2 to 4, values are highlighted in background color of green. 

Left side partition:
1  2
left partition, array's index is from 0 to 1. 

Right side partition -  start and end position. 
6 5 4 
right partition, array's index is from 2 to 4. 

And then the pivot value 3 is inserted in-between left partition and right partition. 
1, 2, 3,  6, 5, 4

Let us recap what we practice here, go over a simple test case, and then choose a pivot value, and then partition, divide and conquer, go to solve two small problems using recursive calls. 

Julia's practice:
Quick sort algorithm writing in C# - less than 20 minutes (18 minutes to write the code with comments)


Quicksort Lecture Study


Julia, spend one hours to read the webpage, and enjoy the great lecture. Reading is much important than coding. And then, work on some questions in the lecture.

princeton lecture notes

Questions and Answers


February 3, 2016

After reading her favourite lecture notes, Julia likes to respond something to entertain quicksort algorithm practice, put something together with her own thinking based on discussion from reading material:

Fact 1:
Q1. Quick sort algorithm will work even in worst case, no dead loop. Why? 
Arguments:
1. Each recursive function, at least one value is resolved, no more work for the value. That is pivot point. So, at most, each recursive call solves one value, n recursive call will solve all n values in the array. Is it fun to design the algorithm? Yes.

Q2. Quicksort function design - only solve one value to position correctly, in the whole array. This is not efficient? 
Arguments:
1. This is the fact. And it works fine. And if you remember that, you can write a quick sort algorithm in less than 5 minutes. 
2. So, position one value, partition array into two sections. 

Q3. Partition can use different strategies, only important and should work on more is to find one, work out as fast as you can. And make it easy to remember. What is your advice? 
Advice:
  Choose last one in the array as a pivot point, and then, find the partition - swap and maintain a partition (each value in the partition  >  pivot value) just on right side of array. Use two pointers to find the partition two values - start, end position. 

Q4. Is this algorithm using in-place without extra space
Answer: 
Yes, the answer is staying in the original array, no extra space (O(1), not O(n)). So, array is used to store result, only swap two nodes' value if need. 

Facts: at most how many swap?     it depends. O(n^2)
           How many recursive calls?  n 

Because it is using in-place, the quicksort function design takes 3 input arguments, original array, start, end. 

If you cannot come out best design using in-place, you may need extra time. 

Feb. 4, 2016 Q5. Work on Leetcode - partition list
86. Partition list
http://www.cnblogs.com/springfor/p/3862392.html

328. Odd Even Linked List


Follow up after 9 months


Nov. 24, 2016
Review the code review about quick sort
Answer the question, link is here.  


Follow up after 13 months


March 14, 2017
1. Read all algorithms in the blog, the blogger got Google and Linkedin offer. The algorithms may be a good study material for Julia.

2. Practice one more time, C# code. Add test case for partition method and quicksort method.