题解 | #最长上升子序列(三)#
最长上升子序列(三)
https://www.nowcoder.com/practice/9cf027bf54714ad889d4f30ff0ae5481
C++, 贪心 + 二分查找
设置一个记录上升子序列状态的二维dp[n + 1][ ];
vector<vector
> dp_str(n + 1, vector ());
第一维下标代表当前长度,其第二维代表最长子序列
贪心思想:若想让子序列长度更长,则应确保每次增加新元素的幅度尽可能的小,“小步多走”;
从头遍历数组,寻找 ==以当前元素为最后元素== 的上升子序列;
- 若 当前元素 > 当前最长子序列的最后一个元素,则添加并更新最长子序列;
- 若 当前元素 <= 当前最长子序列的最后一个元素, 则其可以替代当前最长子序列中间的某一个元素,为后面的上升序列做铺垫
- 二分查找 在当前最长子序列中 查找 【第一个小于当前元素的元素位置pos】,并在其后替换更新
- 按字典顺序大小 比较 【新取得的子序列temp 】 与 【已有的同长度的子序列dp_str[pos + 1]】 哪个更小,保留字典序列小的;
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<vector<int>> dp_str(n + 1, vector<int> ()); // 初始化第一个长度为1的开始子序列 int len = 1; dp_str[len].emplace_back(arr[0]); for (int i = 1; i < n; ++i) { // 若 当前元素 > 当前最长子序列的最后一个元素,则添加并更新最长子序列 if (arr[i] > dp_str[len].back()) { ++len; dp_str[len] = dp_str[len - 1]; dp_str[len].emplace_back(arr[i]); } // 若 当前元素 < 当前最长子序列的最后一个元素, 则其可以替代最长子序列中间的某一个元素 else { // 二分查找 在当前最长子序列中 查找 第一个小于当前元素的元素位置,并更新 int l = 1, r = len, pos = 0; while (l <= r) { int mid = l + ((r - l) >> 1); if (dp_str[mid].back() < arr[i]) { l = mid + 1; pos = mid; } else { r = mid - 1; } } // temp 为 若成功替换后,新的以arr[i]结尾,长度为pos + 1 的子序列 vector<int> temp = dp_str[pos]; temp.emplace_back(arr[i]); // 与以及存在的 同长度的 子序列 按字典大小比较,若新的更小,则更新 if (temp < dp_str[pos + 1]) { dp_str[pos + 1] = temp; } // // 否则, 只更新最后一个元素 // else { // dp_str[pos + 1].back() = arr[i]; // } } } return dp_str[len]; } };