LeetCode 1713. 得到子序列的最少操作次数

难度:困难。
标签:贪心,数组,哈希表,二分查找。

首先,这道题就是最长公共子序列,我竟然没想到。(虽然会超时)

看了两条提醒:
1.The problem can be reduced to computing Longest Common Subsequence between both arrays.
2. Since one of the arrays has distinct elements, we can consider that these elements describe an arrangement of numbers, and we can replace each element in the other array with the index it appeared at in the first array.

然后,题目变成了求最长升序序列。只想到了用动态规划,但是到每一步i,需要遍历0-i的所有元素,比较复杂,超时了。

超时代码:

class Solution {
public:
    int minOperations(vector<int>& target, vector<int>& arr) {
        unordered_map<int, int> maps;
        for(int i = 0; i < target.size(); ++i)maps[target[i]] = i;

        vector<int> new_arr;
        for(int i = 0; i < arr.size(); ++i){
            int a = arr[i];
            if(maps.find(a) != maps.end()){
                new_arr.emplace_back(maps[a]);
            }
        }
        // for(int i = 0; i < new_arr.size(); ++i)cout << new_arr[i] << " ";
        // cout << endl;
        if(new_arr.size() <= 1)return target.size() - new_arr.size();
        vector<int> dp(new_arr.size());
        dp[0] = 1;
        int ans = 0;
        for(int i = 1; i < new_arr.size(); ++i){
            int temp = 0;
            for(int j = 0; j < i; ++j){
                if(new_arr[j] < new_arr[i]){
                    temp = max(temp, dp[j]);
                }
            }
            dp[i] = temp + 1;
            ans = max(ans, dp[i]);
        }
        return target.size() - ans;
    }
};

再看最后一条提醒:
3. Then the problem is converted to finding Longest Increasing Subsequence in the second array, which can be done in O(n log n).

看了题解,将arr映射为索引后,将target也用索引表示,这是一个最长公共子序列问题。
但是,由于target是递增的,因此问题是最长递增子序列。

使用贪心+二分查找来完成这个题。
贪心思想:最长递增序列中同等位置的值尽可能小。
d[i] ,表示长度为 i 的最长上升子序列的末尾元素的最小值。
遍历arr(不在target中的元素被忽略,在target中的元素被替换成索引idx),每次在d中查找第一个大于等于idx的值,如果这个值存在,则这个值替换为索引更小的idx,如果不存在,说明d中的索引都比idx小,直接将idx加入d即可。
最终最长递增序列的长度为d的大小。

相似题:300. 最长递增子序列

正确解法:

class Solution {
public:
    int minOperations(vector<int>& target, vector<int>& arr) {
        unordered_map<int, int> maps;
        for(int i = 0; i < target.size(); ++i)maps[target[i]] = i;

        vector<int> d;
        for(int i = 0; i < arr.size(); ++i){
            int a = arr[i];
            if(maps.find(a) != maps.end()){
                int idx = maps[a];
                // 在d中查找第一个大于等于idx的值
                auto it = lower_bound(d.begin(), d.end(), idx);
                if(it != d.end()){
                    *it = idx;
                }
                else{
                    d.emplace_back(idx);
                }
            }
        }
        return target.size() - d.size();
    }
};

结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值