leetcode
leetcode 1551 ~ 1600
得到子序列的最少操作次数

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

难度:

标签:

题目描述

代码结果

运行时间: 149 ms, 内存: 40.3 MB


/* 思路: 与普通Java实现类似, 我们使用Java Stream API简化代码.

   我们仍然使用一个map记录target中每个元素的位置. 然后通过Stream操作过滤出arr中的有效元素, 并映射到target中的位置. 最后我们计算这些位置的最长递增子序列(LIS), 并计算需要插入的次数. 
*/
import java.util.*;
import java.util.stream.Collectors;

public class SolutionStream {
    public int minOperations(int[] target, int[] arr) {
        Map<Integer, Integer> map = IntStream.range(0, target.length)
                .boxed()
                .collect(Collectors.toMap(i -> target[i], i -> i));

        List<Integer> lis = Arrays.stream(arr)
                .filter(map::containsKey)
                .map(map::get)
                .collect(Collectors.toCollection(ArrayList::new));

        List<Integer> result = new ArrayList<>();
        for (int num : lis) {
            int pos = Collections.binarySearch(result, num);
            if (pos < 0) {
                pos = -(pos + 1);
            }
            if (pos == result.size()) {
                result.add(num);
            } else {
                result.set(pos, num);
            }
        }
        return target.length - result.size();
    }
}

解释

方法:

该题解采用了贪心算法和最长递增子序列(LIS)的概念。首先,创建一个映射将 target 中的值和其索引关联起来。然后,遍历 arr 数组,保留那些在 target 中出现的元素,并将它们转换为在 target 中的索引,存入一个新的数组 lis 中。接下来,利用贪心和二分查找的方法来找出 lis 中的最长递增子序列的长度。最后,由于 target 是一个没有重复元素的数组,且要求 arr 的子序列与 target 相同,因此 arr 至少需要添加的元素个数等于 target 的长度减去 lis 的最长递增子序列的长度。

时间复杂度:

O(n + m logk)

空间复杂度:

O(n + k)

代码细节讲解

🦆
在使用贪心算法和最长递增子序列(LIS)求解时,为什么首先需要将arr数组中的元素转换为它们在target中的索引?
在此题解中,将arr数组中的元素转换为它们在target中的索引是为了简化问题,使其转变为寻找最长递增子序列的问题。这种转换是必要的,因为我们的目标是找到arr中与target相匹配的最长子序列。通过将target的元素映射到它们的索引,我们可以将问题转化为在这些索引上寻找最长递增子序列。这样一来,任何在arr中以递增顺序排列的索引序列都直接对应于target中的一个有效子序列,这样可以有效地利用已有的LIS算法来解决问题。
🦆
为什么在处理LIS时选择使用二分查找方法,直接追加到dp数组的尾部或替换已存在的最小可能值有什么优势?
在处理LIS问题时,使用二分查找方法来更新dp数组可以显著提高算法的效率。传统的LIS算法通过遍历每个元素并与dp数组中的所有元素进行比较,其时间复杂度为O(n^2)。然而,通过使用二分查找来确定元素插入的位置,可以将每个元素的更新时间减少到O(log n),从而将整体时间复杂度降低到O(n log n)。这种方法允许我们快速找到并更新dp数组中的最小可能值,以保持dp数组长度的有效性,即随时维护最长递增子序列的最优长度。
🦆
给定的解法中,dp数组的最终长度代表了什么?它如何决定需要的最小操作次数?
在给定的解法中,dp数组的最终长度代表了在arr中找到的最长递增子序列(该子序列对应于target中的索引)的长度。这个长度是关键,因为它直接决定了为使arr的子序列与target相匹配所需的最少操作次数。具体来说,最少操作次数等于target的长度减去dp数组的长度。这是因为dp数组的长度表示无需任何修改即可从arr中得到的与target相匹配的最大子序列长度,因此剩余的元素需要通过添加操作来补齐以形成完整的target序列。
🦆
如果arr数组完全不包含target数组中的任何元素,该算法的行为将如何变化?
如果arr数组完全不包含target数组中的任何元素,那么在将arr元素转换为target索引的过程中,lis数组将为空,因为没有任何arr元素可以映射到target的索引上。因此,dp数组也将为空,因为没有元素可以用来形成递增序列。在这种情况下,dp数组的长度为0,根据算法的逻辑,最少操作次数将等于target的长度(即len(target) - 0),因为需要将整个target数组作为子序列插入到arr中以满足题目要求。

相关问题