zoukankan      html  css  js  c++  java
  • LeetCode #845 Longest Mountain in Array 数组 线性DP

    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

    Follow up:

    • Can you solve it using only one pass?
    • Can you solve it in O(1) space?



    思路


    解法一

    由题可知,山型数组的 LIS(LDS) 部分是严格递增(递减)的,所以判断条件只能是 < 或 >,不能是 ≤ 或 ≥ 。

    先不考虑什么高级解法,暴力解题。利用一个 for 循环得到每个 increasing sequence 的最后一个元素,称之为 peak,并逐一加入 peak_points 中。随后遍历 peak_points 中的每一个 peak, 继续计算以它为起点的 decreasing sequence 的长度,同时计算出山型数组最长的长度。

    时间复杂度:O(n^2),因为得到了每一个 peak 之后还需要一个循环来处理 decreasing sequence
    空间复杂度:O(n),需要一个 HashMap 存储 peak 的索引和以它为终点的 increasing sequence 的长度

    耗时 28 ms, Memory 9.7 MB, ranking 15%

    class Solution {
    public:
        int longestMountain(const vector<int> &nums) {
            if (nums.size() < 3) return 0;
            
            // mapping from index of peak element to increasing sequence length
            unordered_map<int, int> peak_points;  
            int incre_seq_length = 1;
            for (int i = 1; i < nums.size(); ++i) {
                if (nums[i-1] < nums[i]) {
                    ++incre_seq_length;
                } else {
                    // length of increasing part of B should be at least 2
                    if (incre_seq_length >= 2) {
                        peak_points[i - 1] = incre_seq_length;
                    }
                    
                    incre_seq_length = 1;
                }
            }
            
            // derive length of decresing sequences following start_point
            int max_mountain_length = 0;
            for (auto item : peak_points) {
                int mountain_length = item.second;
                bool has_decre_element = false;
                
                for (int i = item.first + 1; i < nums.size(); ++i) {
                    if (nums[i-1] > nums[i]) {
                        if (!has_decre_element) has_decre_element = true;
                        ++mountain_length;
                    } else {
                        break;
                    }
                }
                
                if (has_decre_element && 
                        max_mountain_length < mountain_length) {
                    max_mountain_length = mountain_length;
                }
            }
            
            return max_mountain_length;
        }
    };
    



    解法二

    利用 DP 求 LIS 的思想求解,逆向求 LIS(相当于求出了一个山型数组的LDS),然后再正向求 LIS,在正向求解时顺便也计算出 mountain_length 。

    利用 up[] 和 down[] 记录了求解 LIS 和 LDS 时每个子序列的长度。up[i] 表示以 nums[i] 为结尾的 increasing sequence 的长度(实际为长度减一,以便计算山型数组的长度),down[i] 表示以 nums[i] 为开头的 decreasing sequence 的长度。

    时间复杂度:O(n),因为求解 LDS 和 LIS 是线性DP
    空间复杂度:O(n),需要用到 up[] 和 down[] 两个记录数组

    耗时 20 ms, Memory 9.0 MB, ranking 91.26%

    class Solution {
    public:
        int longestMountain(const vector<int> &nums) {
            if (nums.size() < 3) return 0;
            
            int max_mountain_length = 0;
            // up[i] represents length - 1 of increasing sequence that end with up[i]
            vector<int> up(nums.size());
            // down[i] represents length - 1 of decreasing sequence that start with down[i]
            vector<int> down(nums.size());
            
            for (int i = nums.size() - 2; i >= 0; --i) {
                if (nums[i] > nums[i+1]) {
                    down[i] = down[i+1] + 1;
                }
            }
            
            for (int i = 1; i < nums.size(); ++i) {
                if (nums[i-1] < nums[i]) {
                    up[i] = up[i-1] + 1;
                    
                    if (down[i] > 0) {
                        int mountain_length = up[i] + down[i] + 1;
                        if (max_mountain_length < mountain_length) {
                            max_mountain_length = mountain_length;
                        }
                    }
                }
            }
            
            return max_mountain_length;
        }
    };
    



    解法三

    follow up 中需要将 space 优化成 O(1),所以不能使用解法二中提到的记录数组 up[] 和 down[]。
    因为每次更新状态时,up[] 和 down[] 中的值不能直接被几个变量替代,所以不能直接压缩空间为 O(1),而是需要重新设计 DP 算法里的状态。

    用 up 表示以 nums[i] 为结尾的 increasing sequence 的长度(实际为长度减一,以便计算山型数组的长度),用 down 表示以 nums[i] 为结尾的 decreasing sequence 的长度。这样,山型数组的长度就等价于 up + down + 1。

    因为 nums 的第一个元素肯定不是 peak (山型数组至少要三个数据,如果第一个元素是 peak,那就只剩山型数组的右半部了),所以从 i=1 开始遍历 nums,计算 up、down 和山型数组长度,在每一次重新爬坡时把 up 和 down 重置为 0。遍历结束后,我们就得到了山型数组最长的长度。

    时间复杂度:O(n)
    空间复杂度:O(1)

    耗时 20 ms, Memory 7.8 MB, ranking 91.26%

    class Solution {
    public:
        int longestMountain(const vector<int> &nums) {
            if (nums.size() < 3) return 0;
            
            int max_mountain_length = 0;
            // up represents length - 1 of increasing sequence that end with current idx
            int up = 0;
            // down represents length - 1 of decreasing sequence that start with current idx
            int down = 0;
            
            // nums[0] cannot be a peak element
            for (int i = 1; i < nums.size(); ++i) {
                if (nums[i-1] == nums[i]) {  // go with a new mountain sequence
                    up = 0;
                    down = 0;
                    continue;
                }
                
                if (nums[i-1] < nums[i]) {
                    if (down > 0) {  // go with a new mountain sequence
                        up = 0;
                        down = 0;
                    }
                    
                    ++up;
                }
                
                if (nums[i-1] > nums[i]) {
                    ++down;
                }
                
                if (up > 0 && down > 0) {
                    int mountain_length = up + down + 1;
                    if (max_mountain_length < mountain_length) {
                        max_mountain_length = mountain_length;
                    }
                }
            }
            
            return max_mountain_length;
        }
    };
    



    参考




  • 相关阅读:
    团队作业(三)
    第四章学习笔记
    2.3.1测试
    缓冲区溢出漏洞实验
    第三章学习笔记
    团队作业(二)
    第十一章学习笔记
    第7,8章自学笔记
    stat命令实现—mystat
    第五章学习笔记
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/12686673.html
Copyright © 2011-2022 走看看