牛课题霸-牛妹的面试-最长递增子序列

牛课题霸-牛妹的面试-最长递增子序列

众所周知,牛妹是一个offer收割姬,这次面试她遇到了这样的一个问题。
给了一个序列,让找出最长的“凸子序列”
何为“凸子序列”:数列中有一个xi,使得所有x0<x1<x2….xi-1<xi且xi>xi+1>xi+2>….>xn
eg:12345431,是山峰序列,12345234不是山峰序列
注:单调递增或单调递减序列也算山峰序列;单独一个数是长度为1的山峰序列

输入
[1,2,3,6,1]

输出
5

备注
给定的序列中数都大于0 且不超过10000,且序列长度不超过1000

这是一个找最长递增子序列的问题,用dp[n]记录以数组中每一个数字结尾的最长子序列,正向遍历算一遍,反向遍历算一遍,就可以得到以某个数字为山峰,左右山坡的长度,从而得到凸序列的长度。

最长递增子序列的O(n2)解法

用dp[i]表示xi结尾的最长递增子序列长度,易知dp[0] = 1,此后dp[k]等于前方max(dp[j])+1 ( 当 j < k 且 xj < xk )。因为以每个位置结尾的dp[i]都要求,且每求一个dp[i]都要往前遍历dp[j],所以复杂度为O(n2)。

最长递增子序列的O(nlogn)解法

每个dp[i]都要求,所以外层复杂度为O(n)的循环是不可避免的,但是计算每一个dp[i]的复杂度可以降至logn。
用一个模拟栈st[]记录当前的递增序列,当xi > st.top,则入栈,st.size则是以xi结尾的最长递增子序列长度。
当xi < st.top,在st[]中用二分法找到第一个≥xi的值,将其替换成xi,并且此时st[]中xi到栈底的长度为最长递增子序列长度,这个的意义就是,st[]中xi以前的数,就是以xi结尾的递增序列中需要的数。
那么被替换的数有没有可能出现在后面的递增序列呢?答案可能会,但不必担心。设被替换的数是A,替换成B,假如后面出现一个数C>A且需要用到以A结尾的递增序列组成更长的递增序列,由于A>B,所以C>B,并且因为以B结尾的递增序列长度等于以A结尾的,所以C的序列用A和用B结果是一样的,不必担心替换问题。
如果要求出这个序列就有问题了,因为最长序列不一定是最后出现的,替换的时候就有可能修改最长序递增列的序列值。
求序列的方法可以在入栈的时候记录栈中序列。


class Solution {
public:
    int mountainSequence(vector<int>& numberList) {
        int ans = 0;
        int len = numberList.size();
        vector<int> st;
        vector<int> dp(len);
        st.push_back(numberList[0]);
        dp[0] = 1;
        for(int i = 1; i < len; i++) {
            if(numberList[i] > st.back()) {
                //入栈
                st.push_back(numberList[i]);
                dp[i] = st.size();
            } else {
                auto it = lower_bound(st.begin(), st.end(), numberList[i]);
                //替换
                *it = numberList[i];
                dp[i] = it-st.begin()+1;
            }
        }
        st.clear();
        st.push_back(numberList[len-1]);
        ans = dp[len-1];
        for(int i = len-2; i >= 0; i--) {
            if(numberList[i] > st.back()) {
                st.push_back(numberList[i]);
                int temp = st.size()+dp[i]-1;
                ans = max(ans, temp);
            } else {
                auto it = lower_bound(st.begin(), st.end(), numberList[i]);
                *it = numberList[i];
                int temp = it-st.begin()+dp[i];
                ans = max(ans, temp);
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值