《LeetCode之每日一题》:108.得到子序列的最少操作次数

Posted 是七喜呀!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《LeetCode之每日一题》:108.得到子序列的最少操作次数相关的知识,希望对你有一定的参考价值。

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


题目链接: 得到子序列的最少操作次数

有关题目

给你一个数组 target ,包含若干 互不相同 的整数,以及另一个整数数组 arr ,arr 可能 包含重复元素。

每一次操作中,你可以在 arr 的任意位置插入任一整数。
比方说,如果 arr = [1,4,1,2] ,那么你可以在中间添加 3 得到 [1,4,3,1,2] 。
你可以在数组最开始或最后面添加整数。

请你返回 最少 操作次数,使得 target 成为 arr 的一个子序列。

一个数组的 子序列 指的是删除原数组的某些元素(可能一个元素都不删除),同时不改变其余元素的相对顺序得到的数组。
比方说,[2,7,4][4,2,3,7,2,1,4] 的子序列(加粗元素),但 [2,4,2] 不是子序列。
示例 1:

输入:target = [5,1,3], arr = [9,4,2,3,4]
输出:2
解释:你可以添加 51 ,使得 arr 变为 [5,9,4,1,2,3,4] ,target 为 arr 的子序列。
示例 2:

输入:target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1]
输出:3
提示:

1 <= target.length, arr.length <= 10^5
1 <= target[i], arr[i] <= 10^9
target 不包含任何重复元素。

题解

方法一:哈希表 + 贪心 + 二分查找

思路:
①最少操作次数等于target.size()减去arr中两者
最长公共子序列的长度
②最长公共子序列的长度,我们可以使用动态规划求解,时间复杂度为O(MN)
③注意到target中无重复元素,我们使用映射,将target[i]映射为其下标,同时将arr中与target重合的元素也映射
由于数组target'递增,我们即求解在最长公共子序列中的部分也必须是严格单调递增的,因此问题可进一步地转换成求
arr'的最长递增子序列的长度
struct HashTable{
    int key,val;
    UT_hash_handle hh;
};

int lower_bound(int* d, int l, int r, int target){
    while(l < r){
        int mid = (l + r) >> 1;
        if (d[mid] >= target){
            r = mid;
        } else {
            l = mid + 1;
        }
    }
    return r;
}
int minOperations(int* target, int targetSize, int* arr, int arrSize){

    //映射到哈希表中
    struct HashTable* hashTable = NULL;
    for (int i = 0; i < targetSize; i++){
        struct HashTable* tmp;
        HASH_FIND_INT(hashTable,&target[i],tmp);
        if (tmp == NULL){
            tmp = (struct HashTable*)malloc(sizeof(struct HashTable));
            tmp->key = target[i], tmp->val = i;
            HASH_ADD_INT(hashTable,key,tmp);
        }
    }

    int d[arrSize],dSize = 0;
    //开辟d数组, d[i]表示长度为 i 的最长上升子序列的末尾元素的最小值,
    for (int i = 0; i < arrSize; i++){
        struct HashTable* tmp;
        HASH_FIND_INT(hashTable,&arr[i],tmp);
        if (tmp != NULL){
            int id = tmp->val;
            int it = lower_bound(d,0,dSize,id);
            if (it != dSize){
                d[it] = id;
            } else{
                d[dSize++] = id;
            }
        }
    }
    return targetSize - dSize;
}

C++

class Solution {
public:
    int minOperations(vector<int> &target, vector<int> &arr) {
        int n = target.size();
        unordered_map<int, int> pos;
        for (int i = 0; i < n; ++i) {
            pos[target[i]] = i;
        }
        vector<int> d;
        for (int val : arr) {//0,2,3
            if (pos.count(val)) {//1,0,5,4,2,0,3
            //找出关键码val(key)
                int idx = pos[val];

                //从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end
                auto it = lower_bound(d.begin(), d.end(), idx);
                if (it != d.end()) {
                    *it = idx;//d[it - d.begin()] = idx;
                } else {
                    d.push_back(idx);
                }
            }
        }
        return n - d.size();
    }
};

以上是关于《LeetCode之每日一题》:108.得到子序列的最少操作次数的主要内容,如果未能解决你的问题,请参考以下文章

《LeetCode之每日一题》:264.递增的三元子序列

《LeetCode之每日一题》:107.最长递增子序列

《LeetCode之每日一题》:154.最长递增子序列的个数

《LeetCode之每日一题》:106.最长公共子序列

《LeetCode之每日一题》:198.最长定差子序列

leetcode每日一题-1218:最长定差子序列