跳至主要内容

Trapping Rain Water

leetcode problem: Trapping Rain Water

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

common problem analysis method

Inspired by this Chinese blog, I list some generalized rules to solve a problem and help us thinking. I will try to use those rules to analyze this problem.

  • generalize condition: abstraction can let us skim the detail of problem and focus on main obstacle. More than that, abstraction can also help us draw conclusion/common solution from this kind of problems
  • make condition more accurate: use some special case to inspire us how to solve it
  • induce from result: the result may contains some more conditions to help us
  • similar problem: the solution, the process to solve a similar problem may provide us some clues to solve this one
  • adjust the conditions of problem( delete a condition, add a condition, change requirement): by this way, we can feel the inner connection between conditions, and how we can use them.
  • divide and conquer

analysis process

Essence under description

What makes a hallow to trap some rain?

The first important question we have to think about is what makes a hallow to trap some rain? What this conclusion is ask us to do can be seen as summing all rain in every single hallow( induce from result; divide and conquer).

  • First thought – climb up, climb down
int now = height[nowI], next = height[nextI];
if (now > next) {
    // start found, remember
} else if (now < next) {// end found
    rain += collect(start, end);
}

But using this way can cause many fake start point(s2) and fake end point(e2) as the following shows:

 s1             e1
____           ____  
    |s2     e2 | 
    ----   ---- 
       |   |
       -----
  • start and end is higher
    Aware of the above the example, we should understand our hallow should be the longest possible sub-array, which make start and end higher than( have to be >) any point in the [start, end).
----     ----
    |   |
    -----
how to decide a hallow

Next problem is how computer can decide the start and end index? Computer can’t just see here is higher and there is lower. It can only scan the array. In the above example, we can also see that the end can decide the start: the end is higher, we may find a new start point to fit it. So the pseudo-code should be:

if (now < next) { // meet a possible end
    // find a start -- back trace
    // sum some rain
}

Similar problem

Analysis goes into this point, we come up with a similar problem: longest paren problem

Given a string containing just the characters ‘(’ and ‘)’, find the length of the longest valid (well-formed) parentheses substring.
For “(()”, the longest valid parentheses substring is “()”, which has length = 2.
Another example is “)()())”, where the longest valid parentheses substring is “()()”, which has length = 4.

In paren problem, we are going to find longest sub-array that start with ‘(’ and end with ‘)’, and the number of ‘(’ in [start, x) must large or equal to that of ‘)’. And if we find a ‘)’, we may find a paired ‘(’ by back-trace. Doesn’t this description sounds familiar?

go down <--> '('
go up <--> ')'
find start index to extend hallow <--> find '(' to extend paired paren

So we may learn some thought/details from the previous solution.

Solution

Finally, it’s the detail of solution, which is the extension of previous pseudo-code. What we should notice is how to find start index when meet an end.
Though above discussion, we can see the following fact:

  • start is in [0, end - 1)
  • if height[start] < height[end], height[start] must be the highest in [0, end); otherwise, we may find a higher start
  • if x is in [0, end - 1), height[x] >= height[end], we are done: x is start.

We can easily turn above description into a linear back-trace code to finish this problem like following:

// return start index or -1 if no start available
int findStartSimple(vector<int>& height, int h, int i) {
    int max = 0;
    int mI = -1;
    for(int j = i - 1; j >= 0; j--) {
        if(height[j] >= h) {
            return j;
        }
        if(max < height[j]) {
            max = height[j];
            mI = j;
        }
    }
    if(mI == i - 1) {
        mI = -1;
    }
    return mI;
}

Or we can solve it in another dimension: height.

We can find all index in [0, end), at which height is larger than or equal to end. Then find the max index, if exist, which will be start. If all point in [0, end) are shorter than end, we just find the max.

int findStartMap(map<int, vector<int>>& h2i, int h, int i) {
    // try to find larger one first
    auto sameOrLarger = h2i.lower_bound(h);
    auto tmp = sameOrLarger;
    auto less = --tmp;
    int maxI = -1;
    while(sameOrLarger != h2i.end()) {
        vector<int>& larger = sameOrLarger->second;
        // find a larger one that before it
        auto index = lower_bound(larger.begin(), larger.end(), i);
        if (index != larger.begin()) {
            int tmp = *(index - 1);
            if(tmp > maxI) {
                maxI = tmp;
            }
        }
        sameOrLarger++;
    }
    if(maxI != -1) {
        return maxI;
    }

    while(less != h2i.begin()) {
        vector<int>& smaller = less->second;
        auto index = lower_bound(smaller.begin(), smaller.end(), i);
        if (index != smaller.begin()) {
            int tmp = *(index - 1);
            if(tmp == i - 1) {
                return -1;
            } else {
                return tmp;
            }
        }
        less--;
    }
    return -1;
}

What should be noticed is map solution may not be faster. The simple back-trace solution pass the test of leetcode but the map solution failed with TLE.

Full source can be found here.

Written with StackEdit.

评论

此博客中的热门博文

Spring Boot: Customize Environment

Spring Boot: Customize Environment Environment variable is a very commonly used feature in daily programming: used in init script used in startup configuration used by logging etc In Spring Boot, all environment variables are a part of properties in Spring context and managed by Environment abstraction. Because Spring Boot can handle the parse of configuration files, when we want to implement a project which uses yml file as a separate config file, we choose the Spring Boot. The following is the problems we met when we implementing the parse of yml file and it is recorded for future reader. Bind to Class Property values can be injected directly into your beans using the @Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via @ConfigurationProperties. As the document says, there exists three ways to access properties in *.properties or *.yml : @Value : access single value Environment : can access multi

Elasticsearch: Join and SubQuery

Elasticsearch: Join and SubQuery Tony was bothered by the recent change of search engine requirement: they want the functionality of SQL-like join in Elasticsearch! “They are crazy! How can they think like that. Didn’t they understand that Elasticsearch is kind-of NoSQL 1 in which every index should be independent and self-contained? In this way, every index can work independently and scale as they like without considering other indexes, so the performance can boost. Following this design principle, Elasticsearch has little related supports.” Tony thought, after listening their requirements. Leader notice tony’s unwillingness and said, “Maybe it is hard to do, but the requirement is reasonable. We need to search person by his friends, didn’t we? What’s more, the harder to implement, the more you can learn from it, right?” Tony thought leader’s word does make sense so he set out to do the related implementations Application-Side Join “The first implementation

Implement isdigit

It is seems very easy to implement c library function isdigit , but for a library code, performance is very important. So we will try to implement it and make it faster. Function So, first we make it right. int isdigit ( char c) { return c >= '0' && c <= '9' ; } Improvements One – Macro When it comes to performance for c code, macro can always be tried. #define isdigit (c) c >= '0' && c <= '9' Two – Table Upper version use two comparison and one logical operation, but we can do better with more space: # define isdigit(c) table[c] This works and faster, but somewhat wasteful. We need only one bit to represent true or false, but we use a int. So what to do? There are many similar functions like isalpha(), isupper ... in c header file, so we can combine them into one int and get result by table[c]&SOME_BIT , which is what source do. Source code of ctype.h : # define _ISbit(bit) (1 << (