给你一个数组 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:
```txt
输入:target = [5,1,3], arr = [9,4,2,3,4]
输出:2
解释:你可以添加 5 和 1 ,使得 arr 变为 [5,9,4,1,2,3,4] ,target 为 arr 的子序列。
```
示例 2:
```txt
输入:target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1]
输出:3
```
提示:
1 <= target.length, arr.length <= 10^51 <= target[i], arr[i] <= 10^9target不包含任何重复元素。
得到子序列的最少操作次数
题目
给你一个数组 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:
1 | 输入:target = [5,1,3], arr = [9,4,2,3,4] |
示例 2:
1 | 输入:target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1] |
提示:
-
1 <= target.length, arr.length <= 10^5 -
1 <= target[i], arr[i] <= 10^9 target不包含任何重复元素。
题解
方法一:
思路
惊喜一遍过。
一个类似最长上升子序列的问题
初步思路
求得arr中的一个子序列,该序列也是target中的一个子序列,只要让这个子序列尽可能的长,最后的答案便是用arr的长度减该子序列的长度。
设dp[i] 为arr前i个数中与target的最长公共子序列。
可遍历arr中元素,若当前遍历元素为num,则判断:
- 若num不在target中,则dp[i] = dp[i-1]
- 若num在target中,要求$dp[i]=之前遍历过的在target中的元素的最大dp值+1=dp[k]+1,k \in [0,i) \wedge k \in target$
时间复杂度是O(n^2)
可以采用最长上升子序列的O(nlogn)算法的优化方式。
优化
设dp[i] ,i是target与arr共同的子序列的长度,dp[i]是该长度下的子序列最后一个元素在target中的下标。dp[0]=-1,因为dp[]是升序的,保持升序可以方便用二分查找。
用哈希表map将每个target中的元素映射为各自对应的下标。
遍历arr数组,当前元素为num:
- 当
num不在map中时,说明该元素不会为增长公共子序列做出贡献。不做处理 - 当
num在map中时,则判断与dp[len]的大小,len是遍历到当前时最长公共子序列长度。- 若
dp[len]<map[num], 在target中当前数字前有len个数字,则最长子序列增长。dp[++len] = map[num] - 若
dp[len]>=map[num], 不能增长,但是可以某长度的子序列末元素下标,对于相同长度的子序列,最后元素的下标越靠前越好。这里可以采用二分查找,找到第一个大于等于当前map[num]的值然后替换。
- 若
最后的答案便是target的长度减去len。
时间复杂度$O(nlogn)$
代码
1 | class Solution { |
方法二:
思路
可以用线段树求当前位置之前的最大递增子序列。
代码
1 | class Solution { |