1713. 得到子序列的最少操作次数
给你一个数组
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
不包含任何重复元素。
通过次数17,007提交次数34,343
法 1 动态规划 — 最长公共子串
找到最长公共子序列的的长度
l
,那么需要插入的数字个数即为 len(target) - l
题解
class Solution: def minOperations(self, target: List[int], arr: List[int]) -> int: target = [-1] + target arr = [-1] + arr # dp[i][j] 子问题:target[:i+1] arr[:j+1] dp = [[0] * len(arr) for _ in target] for i in range(1, len(dp)): for j in range(1, len(dp[0])): # 相等的话, arr[j] 可用亦可不用 if target[i] == arr[j]: dp[i][j] = dp[i-1][j-1] + 1 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) return len(target) - 1 - dp[-1][-1]
法 2 动态规划 - 最长递增子序列
思路
将 arr 中的值映射为 target 中的下标, 因为 target 中的值是唯一的,所以 映射之后的结果是唯一的。
例如
target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1]
, 通过映射后,得到的数组为 nums = [1, 0, 5, 4, 2, 0, 3]
, 现在 求解 nums 的最长递增子序列 的长度,就等价于求 target 和 arr的最长公共在子序列了。为什么呢?首先nums中的数值顺序是按照 arr 来的,其次nums中具体值 如果递增了,那么就是在 target 中逐渐往后的。
arr 映射到 target 的下标 还算 target 映射到 arr 下标,其实是都可以的,直选要 映射过去的 映射数组是唯一的,这几需要有一个数组的值是非重复的,本题中 target 的值是不重复的,所以 arr 映射到 target的下标
题解
class Solution: def minOperations(self, target: List[int], arr: List[int]) -> int: pos = {k:v for v, k in enumerate(target)} nums = [pos[n] for n in arr if n in pos] # 之后问题就转化为 计算 nums 最长递增子序列的长度 if len(nums) <= 1: return len(target) - len(nums) tail = [nums[0]] for i in range(1, len(nums)): if nums[i] > tail[-1]: tail.append(nums[i]) else: l, r = 0, len(tail)-1 while l <= r: m = l + ( r - l) // 2 if tail[m] >= nums[i]: r = m - 1 else: l = m + 1 loc = r + 1 tail[loc] = nums[i] return len(target) - len(tail)