Showing posts with label DP. Show all posts
Showing posts with label DP. Show all posts

Sunday, March 26, 2017

Leetcode 312: burst ballons

March 26, 2017

Introduction 


Problem statement

Julia has some difficulty to figure out dynamic programming from the very beginning for unseen problems, so she likes to vote up virtually the analysis of burst ballons on this blog.

The most important message is like the conversation, let us read the sentence by sentence together here.

"This is the kind of problem we use dynamic programming. WHY? it's very challenging to figure out what's the pattern of optimal burst order. In fact, there's no clear rule that makes sense. Shall we burst the balloon with maximum coins? Or shall we burst the one with least. This is the time we introduce Dynamic Programming, as we want to solve the big problem from small subproblem. It is clear that the amount of coins you gain relies on your previous steps. This is a clear signal of using DP.

The hard part is to define the subproblem. Think out what is clear in this problem? Let's scale this problem down. What is the fact you know for sure? Say if the array has only 1 balloon. The maximum coin would be the coin inside this ballon. This is the starting point! So let's move on to array with 2 balloons. Here, we have 2 cases, which of the balloon is the last one. The last one times the coins in boundary is the gain we get in the end. That is to say, last balloon is the key. Since we don't know the pattern of optimal. We just blindly iterate each balloon and check what's total gain if it's the last ballon."

But after those two paragraphs, Julia got lost the formula:

Let's use dp[i][j] to denote maximum gain from balloon range i to j. We try out each balloon as last burst in this range. Then the subproblem relation would be:

foreach k in i to j:
 dp[j][i] = max(array[j-1]*array[k]*array[i+1] + dp[j][k-1] + dp[k+1][i], dp[j][i]);


Algorithm study


Study the blog about burst ballons first. (March 26, 2017, 11:01am - 11:15am)
Watch the video - burst ballons.  (March 26, 2017, 11:15am - 11:30am, 2:40pm - 3:00pm)
Continue to read Leetcode discussion (March 26, 2017, 12:00pm - 12:30pm)

Thinking process


Continue to read Leetcode discussion (March 26, 2017, 12:00pm - 12:30pm)

Julia is getting smarter, she starts to read more discussions from Leetcode discussion instead of blogs written in Chinese, she values the discussion with vote systems.

Discussion with over 400 up-votes - link is here.

Divide and conquer   vs  reverse thinking

Continue to write C# code using sample code (March 26, 2017, 12:30pm - 12:58pm)

Julia's C# practice, pass all Leetcode test cases. Code link is here.

It is not easy to figure out the dynamic programming solution the first time, what I can do is to read the leetcode discussion again, and follow the thought process, from recursive function, naive solution first, and then try to move to next stage.

Notes from discussions:
1. Coursera - algorithm 2 - optimal binary tree DP problem
2. GeekforGeeks algorithm - optimal binary tree DP problem

Thought process rehearsal 


Julia likes to follow up the thought process closely. That is most important part to learn, how to approach the problem naively first, and then move forward with the hints, and problem solving skills.

The most naive idea the backtracking

We have n balloons to burst, which mean we have n steps in the game. In the ith step we have n - i balloons to burst, i = 0 ~ n - 1. Therefore we are looking at an algorithm of O(n!). Well, it is slow, probably works for n < 12 only.

Well, we can find that for any balloons left the maxCoins does not depends on the balloons already bursted. This indicate that we can use memorization (top down) or dynamic programming (bottom up) for all the cases from small numbers of balloon until n balloons. How many cases are there? For k balloons there are C(n, k) cases and for each case it need to scan the k balloons to compare. The sum is quite big still. It is better than O(n!) but worse than O(2n).


Better idea using recursive and memorization or DP


We then think can we apply the divide and conquer technique? After all there seems to be many self similar sub problems from the previous analysis.
Well, the nature way to divide the problem is burst one balloon and separate the balloons into 2 sub sections one on the left and one one the right. However, in this problem the left and right become adjacent and have effects on the maxCoins in the future.
Then another interesting idea come up. Which is quite often seen in dynamic programming (dp) problem analysis. That is reverse thinking. Like I said the coins you get for a balloon does not depend on the balloons already burst. Therefore instead of divide the problem by the first balloon to burst, we divide the problem by the last balloon to burst.
Why is that? Because only the first and last balloons we are sure of their adjacent balloons before hand!
For the first we have nums[i-1]*nums[i]*nums[i+1] for the last we have nums[-1]*nums[i]*nums[n].
OK. Think about n balloons if i is the last one to burst, what now?

We can see that the balloons is again separated into 2 sections. But this time since the balloon i is the last balloon of all to burst, the left and right section now has well defined boundary and do not affect each other! Therefore we can do either recursive method with memoization or dp.


Final


Here comes the final solutions. Note that we put 2 balloons with 1 as boundaries and also burst all the zero balloons in the first round since they won't give any coins.

The algorithm runs in O(n3) which can be easily seen from the 3 loops in dp solution.


Video Study



Watch the video - burst balloons.  (March 26, 2017, 11:15am - 11:30am, 2:40pm - 3:00pm)

19:08/ 27:01

Burst balloons to maximum value, Julia copied and paste from the video:



Wednesday, December 14, 2016

Leetcode 322: Coin Change - Find minimum number of coins

Dec. 14, 2016

Julia likes to choose most popular post in code review on stackexchange.com, study the post, write down some notes, and also practice to write her own answer. Try to get into the community as active learner, teacher, and hardworking helper.

Her first post to answer question - no response so far, Dec. 14, 2016 8:19pm
http://codereview.stackexchange.com/a/149598/123986

She likes to write a second one:

Problem: 14 votes, 6 answers, 11K views
http://codereview.stackexchange.com/questions/47397/find-minimum-number-of-coins?rq=1

Same problem:
Leetcode:  Coin Change

http://www.cnblogs.com/grandyang/p/5138186.html

http://blog.csdn.net/liyuefeilong/article/details/50687271

Best solution - using DP, bottom up - Temple Ph.D.
https://github.com/jianminchen/LeetCode-Java-Solutions/blob/master/322.coin-change.java

This may be the best solution - written by MSFT employee, ICPC coach - PH.D.
Time complexity:
Space complexity:
https://github.com/jianminchen/LeetCode-17/blob/master/322_v1.cpp

Post the answer on the code review here. Julia, work on reputation target: 50, right now, 45. Once Julia has 50 reputation, she can leave a comment in any post.

Or click the link:
http://codereview.stackexchange.com/a/150130/123986




Monday, November 14, 2016

HackerRank codesprint - Array construction - after contest (series 2 of 5)

Nov. 14, 2016

Problem statement:
https://www.hackerrank.com/contests/university-codesprint/challenges/array-construction/submissions/code/7825209


Study C# submissions:
1. perfect solution with full score 80
https://gist.github.com/jianminchen/096ebc5bc1769b83b38ec6eeaabbc7c5

Julia spent more than one hour to read code, but she could not understand the design. So, she decided to work on debugging, add output text info to figure out the design.

Here are workout she did and then figured out the algorithm:

Study more than 2 hours on one of solutions, using recursive solution; but Julia still are not clear about the solution. Need to work on more! Do not give up! Try it every day 10 minutes. It should be easy! 


Julia's work (3+ hours) Try very hard to understand the clever solution by debugging 

-  From the above C# solution:
https://gist.github.com/jianminchen/096ebc5bc1769b83b38ec6eeaabbc7c5

-->   add some debug information to understand the algorithm 

add debugging information to the source code 


-> Here is the log file to understand the algorithm design:

Question 1:
Use your own words to guess how to design the algorithm through debugging process?

Answer:

still confused about line 118, line 119:

118  int newSum     = sum + i * (n - p);

119  int newDiffSum = diffsum + (i * p - sum) * (n - p);

Question 2: What does (n-p) stand for? Can you explain it in one sentence?

Let us work on one more change first:


so, decided to track n-p value on line 118.
C# code with debug info (stage II):


Actionable Items:

1. Run test cases, and compare the time difference 
First, comment out code on line 36, time out on test case 4.

Write a new blog on this testing adventure.
http://juliachencoding.blogspot.ca/2016/11/hackerrank-university-codesprint-array_18.html


2. Study all C# solutions:

2.1. perfect solution with full score 80

2.2. perfect solution with full score 80

2.3. score half score 40

3. understand one term: constructive algorithm
Constructive algorithm: (preprocessing, and then, lookup)
(HackerRank - array construction is a constructive algo.)

Great idea to push hard - cannot get it wrong! that is the attitude for advanced level algorithm involved mathematical analyse.

Tuesday, July 26, 2016

Lintcode 79: longest common substring

July 26 2016

Work on lintcode: longest common substring.

Problem statement:

Given two strings, find the longest common substring.
Return the length of it.
Note
The characters in substring should occur continiously in original string. This is different with subsequnce.

Algorithm Study

Study the blog - longest common substring (60 minutes reading first time/ 20 minutes review every 6 months) written by a facebook engineer, Ider Zheng. Julia likes the article written in Chinese, it is a well-written and very good thinking process about the problem solving.

Julia likes to repeat the process here in her own words.

1. Brute force solution -> from O(n4) to O(n3), great analysis in the above blog:

Good thinking addes value to your coding practice:

Warmup with a brute force solution:

Time complexity - O(n4)

For example, a string s1 = "abcdefg",

One way to think about brute force:

The length of string is 7.

How many substring in s1? Guess?

Substring - start position and end position, two variables. Each one has O( n ) choices. Total is O(n2) choices.

More detail, start position can be any i from 0 to 6, and then end position starts from i to n-1. The variation formula, Sum = (n - 1) + ( n - 2) + ... + 1 = (n-1) n / 2, so the total is O(n2);

Try to reduce brute force variation from O(n2) to O(n), instead of letting start position and end position
both varies, just work on start position only.

Small improvement based on brute force solution

Time complexity - O(n3)

Second way to think about using brute force:
The start position of substring is from 0 to n-1, so considering the start position, start a new search.

So, the total of search is O(n).

 Longest common substring - start from start-position, and then  compare both of chars are equal, if yes, continue, record length and compare to maximum length, else then break the search.

1. C++ code:

Code is from the blog written by a facebook engineer.

The time complexity is O( n). There is duplicated calculation.

Will write C# practice very soon.

Dynamic programming - optimal time complexity O(n2)


2. Use Dynamic programming, in the above blog, the analysis is very helpful.

Work on dynamic programming, improve time complexity from O(n4) or O(n3) to O(n2), using memorization, space O(n2), bottom up approach.

The idea is to find the formula of DP - dynamic programming.

table T(i, j) - common substrings, using end position as a variable.


one is in s1, ending at position s1[i];
one is in s2, ending at position s2[j].

We know that if T(i, j) >0, then, s1[i] = s2[j];
if(s1[i+1] == s2[j+1]), then, T[i+1, j+1] = T[i,j]+1,
otherwise, T[i+1,j+1] = 0.

Will think about to put together a graph here to explain the idea as well.

2. C++ code:


From blog: C++ code
DP solution, time complexity O(nm), space O(nm)


3. further improvement: C++ code
DP solution, time complexity O(nm), space O(nm) -> O(n+m) -> O(1)
because the recurrence formula tells us that the current position only relies on diagonal position - left-up corner ( i - 1, j - 1)
https://gist.github.com/jianminchen/0061dcf562bd0bdb091301241c38730f

From blog

Julia's practice:
1. brute force solution C#
A. first writing, static analysis catching 1 bug, left 2 bugs for debugging. (not so good!)

B. fix all bugs - final presentation: C#

2. Dynamic programming solution using C#:


Highlights of code writing and execution:

1. static analysis - find bugs
change made: line 56 - 61, if the longest common substring is with length 1 and also start from row 0 or col 0.

line 67 - 72

2. Test case failed on line longestCommonSubstring("abc1def","1ghijkl") - should be "1",

change made: move end1 variable to line 49, and set variable from line 56 - 61, line 67 - line 72

2nd version: add comments


3. DP solution with space reduction: O( nm ) -> O( n+m )  -> O( 1 )

practice later.

* Design issues:
         *
         * 4 variables - memo, longest, end1, searchFirstRowCol
         * 1. memorization using two dimension array - memo
         * 2. variable int longest - get maximum length
         * 3. variable int end1    - string s1 - end position - s1's substring end position
         * 4. variable bool searchFirstRowCol - check first row and first col to update maximum length

DP problems:

Follow up after 8 months


March 17, 2017

1. Read code review on longest common substring algorithm.
2. Read wiki article about "Algorithm Implementation/Strings/Longest common substring"
3. Blog formatting to make it more readable. 
4. Code review:

Ashton and String Hackerrank


Coding practice is like sports - I don't feel fear when I am on court. That's where I feel at home.

Monday, July 25, 2016

Short Palindrome - HackerRank - world codesprint #5

July 25, 2016

Problem statement

Julia spent 3+ hours to score 13 out of 40 points, she enjoyed the time to work on the problem.

 A few issues: wrong answer, time out, run time error.

 More than 3 submissions, failed to solve time out issue.

 1. C# solution

 2. Add memorization using Dictionary to save time, failed to solve issues.
C# solution 

 3. add one more memorization, failed to solve issues.
C# solution

 4. Review code and then add some comment, and analyse design issues:

C# solution

Review the design, try to find the flaws - a few ideas to improve timeout, no run-time error issue.

review code and then add comment for function countingPalindromes:
/*
* use two loops
*
* get 0110 pairs
* get 1001 paris
*
* Think about DP solution - dynamic programming - not working, too complicate
*
* brute force solution:
* 0 - start char, end char, and then, count how many possibilities
* Go through O(n*n) case, for any two 0 in pseudo string, check in-between
*
* For example:
* 001010110
*
* The above case, there are 5 '0',
* position of '0' is 0, 1, 3, 5, 8,
* So, any two '0' combinations are 5*4/2*1 = 10
*
* A: 0, 1, in between, there is no chars, n/a
* B: 0, 3, 2 chars in between - "01", count how many '1' in the substring, only 1.
* C: 0, 5, 4 chars in between - "0101", two one inside, so how many combination
* of 11, only 1 choice.
* D: 0, 8, 7 chars in-between - "0101011", four one's in the substring, how many combination - 4*3/2 = 6 choices
*/

Add comment for function getPseudoString(...)
/*
* using 0 and 1 to construct the string
* 1001
* 0110
*
* There are 26 chars in alphabetic string, any two combination is 26*25/2*1 > 250
* Instead, using 0 and 1 to stand for two distinct characters.
* for example, abab
* 0101
* So, we can conclude one thing:
* ababa
* 01010
* or
* 10101
* So, use memorization, 01010 and 10101 should be same key
* See if this can solve time out issues - July 25, 2016
*
* reverse of string should be same key
*/
It took Julia more than 3 hours to gain 13/40 point, but learning experience was so great, and she enjoyed the coding experience.

Starting from brute force solution, she made some progress to gain 13 points; go through optimal solution - DP, should be next step.

Statistics:

450 score 40/40
1600  score above 12/40
Total submission: 2180

Facts about HackerRank:
aiming brute force, 30% score.

Dynamic solution:

detail from editiorial notes.  

The idea of DP from the above website: string length is n

pattern to search

xyyx

xy ends position at i - iterate from 1 to n-1,

denote l[i]

yx starts at position i - iteration from i to n-1

denote r2[i]

yx starts at position >=i

denote r[i] = r2[i] + r2[i+1]+...+r2[n-1]

So, to get a solution:

   Iterate i from 1 to n-1
 
   sum of l[i] * r[i]

And one more thing,
          r[i] = r2[i] + r[i+1]

The idea Julia tried on July 24, 2016 -
suppose that T[i] is the short palindrome like "xyyx" at end of string i, how to construct T[i+1]?

Follow up after 8 months


C# code


Follow up 


8/25/2017
I go through the code, and evaluate the code. The fact is that the code is too complicate for a medium level algorithm. Based on the past code contest experience, the code should be very short and concise.

I will put some code review on my last practice, and then write a solution to help the dynamic programming algorithm practice and learning. Make it part of 10 step series.