题目描述
给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)
示例1
输入
[2,1,5,3,6,4,8,9,7]
输出
[1,3,4,8,9]
示例2
输入
[1,2,8,6,4]
输出
[1,2,4]
说明
其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个字典序最小,故答案为(1,2,4)
牛客链接:
https://www.nowcoder.com/practice/9cf027bf54714ad889d4f30ff0ae5481?tpId=188&&tqId=35305&rp=1&ru=/ta/job-code-high-week&qru=/ta/job-code-high-week/question-ranking
解题思路
用两步,第一步:先确定最长子序列长度
第二步:确定字典序最小的数组
贪心+二分法可以在O(nlogn)时间内确定每个元素为终点时最长递增子序列。但res不一定是最终顺序,res此时只有长度信息是准确的。
为了获取字典序最小。我们从后往前遍历,取每个位置上最后被替换的元素。
代码:
class Solution {
public:
/**
* retrun the longest increasing subsequence
* @param arr int整型vector the array
* @return int整型vector
*/
vector<int> LIS(vector<int>& arr) {
int len = arr.size();
vector<int> res;
vector<int> lens;
lens.push_back(1);
res.push_back(arr[0]);
//用动态规划获得res的长度,此时res里存的数值不是正确的
for(int i=1; i<len; i++) {
if(arr[i] > res.back()) {
res.push_back(arr[i]);
lens.push_back(res.size());
} else {
//int relPos = lower_bound(res.begin(), res.end(), arr[i])-res.begin();
//res[relPos] = arr[i];
//lens.push_back(relPos);
int j;
for(j=res.size()-1; j>=0; j--) {
if(arr[i] >= res[j])
break;
}
res[++j] = arr[i];
lens.push_back(++j);
}
}
//校正res里的数据
for(int i=len-1,k=res.size()-1; k>=0; i--) {
if(lens[i]-1 == k) {
res[k] = arr[i];
k--;
}
}
return res;
}
};
参考链接:
https://www.nowcoder.com/questionTerminal/9cf027bf54714ad889d4f30ff0ae5481
注意:
lower_bound(),upper_bound()函数的使用
贪心算法,用lens[i]记录当前下标的数字插入后的最长子序列大小