《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
解释:你可以添加 5 和 1 ,使得 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.得到子序列的最少操作次数的主要内容,如果未能解决你的问题,请参考以下文章