LongestMountainInArray [source code]


public class LongestMountainInArray {
static
/****************************************************************************/
class Solution {
    public int longestMountain(int[] A) {
        if (A.length < 3)
            return 0;
        int max_len = 0, N = A.length, anchor = N;
        for (int i = 0; i < N; i++) {
            if (i > 0 && A[i] < A[i - 1]) {
                max_len = Math.max (max_len, i - anchor + 1);
            }
            if (anchor < N && i > 0 && A[i] == A[i - 1]) {
                anchor = N;
            } else if (i < N - 1 && A[i] < A[i + 1] && (i == 0 || A[i] <= A[i - 1])) {
                anchor = i;
            }
        }
        return max_len;
    }
}
/****************************************************************************/

    public static void main(String[] args) {
        LongestMountainInArray.Solution tester = new LongestMountainInArray.Solution();
        int[][] inputs = {
            {2,1,4,7,3,2,5}, {5},
            {2,2,2}, {0},
            {0,1,2,3,4,5,4,3,2,1,0}, {11},
            {1,1,0,0,1,0}, {3},
            {2,3,3,2,0,2}, {0},
        };
        for (int i = 0; i < inputs.length / 2; i++) {
            System.out.println (Printer.separator ());
            int[] A = inputs[2 * i];
            int ans = inputs[2 * i + 1][0], output = tester.longestMountain (A);
            System.out.printf ("%s\n%s\n%d\n", 
                Printer.array (A), Printer.wrap (output + "", output == ans ? 92 : 91), ans
            );
        }
    }
}

哎呀这题, 看起来好像有点难啊, 好像是一个DP题目;

DP题目, 真的真的, 求你了, 别秀了, 不要以为自己脑子里完全抽象的理解就能解出来, 动动手走几个具体的例子求求你了, 头皮发麻;

对于mountain的定义还是比较清晰的; example2给的这个, 我感觉可以一开始主逻辑里面直接忽略, 然后最后找到的mountain(至少能找到一个长度为1的感觉), 最后检查一下是不是足够长度就行了?

当然也可以直接实现的时候就要求长度至少是3, 小问题, 先不纠结了;

走了一下具体的例子, 感觉一个简单的DP应该可以, 随便写写看;

突然发现好像根本不用DP? 直接找到所有的maximal uptick就行了, 一个greedy的做法应该能够搞定;
想想怎么维护信息;

从左到右, uptick上面的点, 应该记忆左边最近的山谷, 然后downtick上面的点, 应该记忆左边最近的山顶; 记忆的都是index; 这样我们当最后走downtick的时候, 每一个点, 找到自己左边最近山顶, 然后自己对应的mountain长度就是index-山顶.left_山谷.
好像可以更简单, 只关注所有的山谷就行了?

看起来有点傻的算法, 写写看;

被test3给break了, 为了解决这个test, 感觉用imply做法好像简单一些;
不过对于这个题目, 直接自己写其实也很简单, 逻辑比较简单;

test4才看出来这个问题的难点了, 如果山谷是平原, 也要考虑的;
怎么修改, 直接两个<那里都加上一个等号呢? 不行的, 因为只有一头可以去等号; 这个怎么表达? 当时425应该是有教了的, 忘记了;
要表达的应该是, 至多有一个等号;

else if (A[i] < A[i + 1] && !equal_used) {  
            right_good = true;  
        }

又中了一次&&header陷阱.

写到这里没有发现一个重要的问题, 比如:1 0 0 1这样的一个, 你发现一个问题没有, 我之前有一个assumption是不对的: 山谷是可以分开的, 这里实际上有一个下降山谷, 和一个上升山谷: 他们不在同一个点上;

这个时候我的代码是:

class Solution {  
    public int longestMountain(int[] A) {  
        if (A.length < 3)  
            return 0;  
        int max_len = 0, N = A.length, prev = -1;  
        for (int i = 0; i < N; i++) {  
            if (good (i, A)) {  
                if (prev < 0) {  
                    prev = i;  
                } else {  
                    max_len = Math.max (max_len, i - prev + 1);  
                }  
            }  
        }  
        return max_len;  
    }  

    boolean good (int i, int[] A) {  
        int N = A.length;  
        boolean left_good = false, right_good = false, equal_used = false;  
        if (i == 0) {  
            left_good = true;  
            equal_used = true;  
        } else if (A[i] <= A[i - 1]) {  
            left_good = true;  
            if (A[i] == A[i - 1])  
                equal_used = true;  
        }  
        if (i == N - 1) {  
            right_good = true;  
            equal_used = true;  
        } else if (A[i] <= A[i + 1]) {  
            right_good = !equal_used || A[i] != A[i + 1];  
        }  
        return left_good && right_good;  
    }  
}

这个就不对了, 因为这个代码的一个隐藏的要求就是山谷必须是唯一的点;

怎么修改? 好难啊; well, 一个mountain的长度是一个下降山谷的位置减去一个上升山谷的位置; 所以好像也是可以解决的;

好像就是一个分段函数就行了;

前面得到的一个教训就是, 如果比如你要判断一个上升沿, 那么这个上升沿的左边是可以取等号的, 右边是不可以的; 这个应该可以用到;

用trigger写法好像有点麻烦, 用run写法来写写看? 对上升沿有一个复杂判断, 至于下降沿, 不管;

这个时候的主循环:

            if (i > 0 && A[i] < A[i - 1]) {  
                max_len = Math.max (max_len, i - anchor + 1);  
            }  
            if (i < N - 1 && A[i] < A[i + 1] && (i == 0 || A[i] <= A[i - 1])) {  
                anchor = i;  
            }

注意两个地方:

  • 不能用else连接, 因为上升沿实际上也可能同时是一个downtick当中的一份子, 所以也要用来更新max_len;
  • 两个if的顺序很重要, 更新max_len一定要在更新anchor之前;

但是这个代码还是被test5给break了; 然后我发现这题好像还是得用DP来写? 不对, 这里当我走到233的时候, 我是不是可以直接把当前这个run给断了?

最后好不容易是AC了, 写的一手好bug;

这题实际上还是挺有意思的, 虽然最后的算法还是挺简单的, 但是我中间还是有大量的思考过程, 很有意思(对, 写bug也很有意思, 不过我之前那个代码的那种bug, 基本就是严重的会导致挂面试的bug了);


UNFINISHED


uwi:5.5min

class Solution {  
    public int longestMountain(int[] a) {  
        int n = a.length;  
        int max = 0;  
        for(int i = 0;i < n;i++){  
            int j = i-1, k = i+1;  
            for(;j >= 0 && a[j] < a[j+1];j--);  
            for(;k < n && a[k-1] > a[k];k++);  
            if(i-j > 1 && k-i > 1){  
                int len = k-j-1;  
                max = Math.max(max, len);  
            }  
        }  
        return max;  
    }  
}

Problem Description

Let's call any (contiguous) subarray B (of A) a mountain if the following properties hold:

  • B.length >= 3
  • There exists some 0 < i < B.length - 1 such that B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]

(Note that B could be any subarray of A, including the entire array A.)

Given an array A of integers, return the length of the longest mountain.

Return 0 if there is no mountain.

Example 1:

Input: [2,1,4,7,3,2,5]  
Output: 5  
Explanation: The largest mountain is [1,4,7,3,2] which has length 5.

Example 2:

Input: [2,2,2]  
Output: 0  
Explanation: There is no mountain.

Note:

  • 0 <= A.length <= 10000
  • 0 <= A[i] <= 10000

Difficulty:Medium
Total Accepted:1.2K
Total Submissions:4.4K
Contributor:

results matching ""

    No results matching ""