贪心+二分
(1)首先,数据范围,完全的dp做法时间复杂度为,肯定超时;
(2)设数组为当前长度为可能的最大递增子序列(不一定是字典序最小的!),可能的原因是该求法可以得到最大递增子序列的最大长度,但是保存的未必是子序列!可以模拟看下样例,以及的过程;
(3)我们贪心地希望能够更长,那么希望已经构成的元素更小!
(4)所以二分从中找第一个大于等于的元素替换,如果没有就说明需要更新长度;
(5)以上做法是标准的贪心+二分求最长递增子序列的长度的做法,本题需要得到最小字典序的子序列,所以在以上基础上进行修改;
(6)我们需要记录表示以当前元素为结尾的最长递增子序列的长度!
(7)根据我们逆序地从最大长度开始遍历,即满足curLen[i] == ans条件的元素,就是长度为ans的字典序最小的方案,即可得到答案;
具体可以拿样例测一下,理解下原理;
时间复杂度:
空间复杂度:

class Solution {
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
        // write code here
        int n = arr.size();
        vector<int> tail(n); //贪心的希望末尾的数字越小越好
        vector<int> curLen(n); //arr的第i个元素对应的最大递增子序列长度
        int ans = 0;
        for(int i = 0; i < n; i ++){
            int left = 0, right = ans;
            while(left < right){ //找第一个大于等于arr[i]的位置
                int mid = left + (right - left) / 2;
                if(tail[mid] >= arr[i]) right = mid;
                else left = mid + 1;
            }
            tail[left] = arr[i];
            curLen[i] = left + 1; //当前元素对应最大递增子序列的长度
            if(left == ans) ans ++;
        }
        vector<int> res(ans);
        for(int i = arr.size() - 1, j = ans; i >= 0; i --){
            if(curLen[i] == j) res[-- j] = arr[i];
        }
        return res;
    }
};