1713. 得到子序列的最少操作次数(最长上升子序列问题)
Posted mp-ui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1713. 得到子序列的最少操作次数(最长上升子序列问题)相关的知识,希望对你有一定的参考价值。
1713. 得到子序列的最少操作次数
难度困难103收藏分享切换为英文接收动态反馈
给你一个数组 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 <= 105
1 <= target[i], arr[i] <= 109
target
不包含任何重复元素。
题解
这道题目求的是最长公共子序列,假设target和arr数组的最长公共子序列为m,那么答案就是target.length - m
但是target数组不包含重复元素,所以可以把他转换为最长上升子序列。
之前求最长上升子序列的时候比较的是数组的值,比如数组[0,1,0,3,2,3]
求得的一个最长上升子序列为[0,1,2,3]
,这个结果得出的依据就是根据数组内部值的大小来判断的,因为0就是比1小,1就是比2小,2比3小。
在这里就可以根据这个来换个思路,求target = [5,1,3], arr = [9,4,2,3,4]
的最长公共子序列,其实就是在求arr = [9,4,2,3,4]
的最大上升子序列,只不过得出结果的依据就不是根据数字的大小,而是根据这个数在target数组里面的下标大小,这样子就能同时兼顾到两个数组。
为了方便得出每一个数在target数组的下标,所以一开始就预处理一下,将下标存到map里面
求最大上升子序列有两种方法,第一种是普通的动态规划,需要O(n^2)的时间复杂度,显然会超时
class Solution {
public int minOperations(int[] target, int[] arr) {
HashMap<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < target.length; i++) {
map.put(target[i],i);
}
//对arr求最长上升子序列
int[] dp = new int[arr.length];
int max = 0;
for (int i = 0; i < arr.length; i++) {
dp[i] = 1;
if(!map.containsKey(arr[i])){
continue;
}
for (int j = i - 1; j >= 0; j--) {
if(map.containsKey(arr[j]) && map.get(arr[i]) > map.get(arr[j])){
dp[i] = Math.max(dp[i],dp[j] + 1);
}
}
max = Math.max(max,dp[i]);
}
return target.length - max;
}
}
第二种方法是维护一个贪心数组,再使用二分查找,这个成功AC:
class Solution {
public int minOperations(int[] target, int[] arr) {
HashMap<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < target.length; i++) {
map.put(target[i],i);
}
//对arr求最大上升子序列
int[] dp = new int[arr.length + 1]; //dp[i]代表长度为i的最大递增子序列的最后一个数字,dp数组是递增的
int size = 0;
for (int i = 0; i < arr.length; i++) {
if(!map.containsKey(arr[i])){
continue;
}
if(size == 0 || map.get(dp[size]) < map.get(arr[i])){
dp[++size] = arr[i];
}else{
//二分查找
int l = 1;int r = size;int mid;
while(l < r){
mid = (r - l) / 2 + l;
if(map.get(dp[mid]) >= map.get(arr[i])){
r = mid;
}else{
l = mid + 1;
}
}
dp[l] = arr[i];
}
}
return target.length - size;
}
}
以上是关于1713. 得到子序列的最少操作次数(最长上升子序列问题)的主要内容,如果未能解决你的问题,请参考以下文章
力扣LeetCode-1713. 得到子序列的最少操作次数-题解-最长递增子序列
力扣LeetCode-1713. 得到子序列的最少操作次数-题解-最长递增子序列
LeetCode1143. 最长公共子序列/300. 最长递增子序列//1713. 得到子序列的最少操作次数(好题!!!!!)