IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> leetcode *1713. 得到子序列的最少操作次数(2021.7.26) -> 正文阅读

[数据结构与算法]leetcode *1713. 得到子序列的最少操作次数(2021.7.26)

【题目】*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 <= 105
1 <= target[i], arr[i] <= 109
target 不包含任何重复元素。

【解题思路1】动态规划 最长递增子序列

会超时

预处理:
由于 target 的元素互不相同,我们可以用一个哈希表记录 target 的每个元素所处的下标,并将 arr 中的元素映射到下标上,对于不存在于 target 中的元素,由于其必然不会在最长公共子序列中,可将其忽略。
示例 2 来说,将 arr 中的元素转换成该元素在 target 中的下标(去掉不在 target 中的元素 7),可以得到一个新数组arr ′ =[1,0,5,4,2,0,3]

动态规划找最长上升子序列:
若将 target 也做上述转换,这相当于将每个元素变为其下标,得target ′ =[0,1,2,3,4,5]
则求原数组的最长公共子序列等价于求上述转换后的两数组的*300. 最长上升子序列

class Solution {
    public int minOperations(int[] target, int[] arr) {
        int n = target.length;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            map.put(target[i], i);
        }

        List<Integer> list = new ArrayList<>();
        for (int val : arr) {
            if (map.containsKey(val)) {
                list.add(map.get(val));
            }
        }

        int m = list.size();
        int[] dp = new int[m];
        int max = 0;
        for (int i = 0; i < m; i++) {
            dp[i] = 1;
            for (int j = 0; j < i; j++) {
                if (list.get(i) > list.get(j)) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            max = Math.max(dp[i], max);
        }

        return n - max;
    }
}

【解题思路2】贪心 + 二分

预处理部分同上。

求dp 贪心 + 二分:
设当前已求出的最长上升子序列的长度为 len(初始时为 1),从前往后遍历数组 nums,在遍历到 nums[i] 时:

  • 如果 nums[I] > dp[len] ,则直接加入到 dp 数组末尾,并更新 len=len+1;
  • 否则,在 dp 数组中二分查找,找到第一个比 nums[i] 小的数 dp[k] ,并更新 dp[k+1] = nums[i]。

以输入序列 [0,8,4,12,2] 为例:
第一步插入 d=[0];
第二步插入 d=[0,8];
第三步插入 d=[0,4];
第四步插入 d=[0,4,12];
第五步插入 d=[0,2,12]。
最终得到最大递增子序列长度为 3。

class Solution {
    public int minOperations(int[] target, int[] arr) {
        int n = target.length;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            map.put(target[i], i);
        }

        List<Integer> list = new ArrayList<>();
        for (int val : arr) {
            if (map.containsKey(val)) {
                list.add(map.get(val));
            }
        }

        int m = list.size();
        if (m == 0) {
            return n;
        }

        int[] dp = new int[m];
        int len = 1;
        dp[0] = list.get(0);
        for (int i = 1; i < m; i++) {
            int num = list.get(i);
            if (num > dp[len - 1]) {
                len++;
                dp[len - 1] = num;
            } else {
            	// 二分查找dp数组中第一个不比num小的数的下标
                int index = binarySearch(dp, len, num);
                dp[index] = num;
            }
        }

        return n - len;
    }

    public int binarySearch(int[] dp, int len, int target) {
        int left = 0, right = len - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            if (dp[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
}

用list代替dp数组稍微精简一下步骤

class Solution {
    public int minOperations(int[] target, int[] arr) {
        int n = target.length;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            map.put(target[i], i);
        }

        List<Integer> dp = new ArrayList<>();
        for (int val : arr) {
            if (map.containsKey(val)) {
                int num = map.get(val);
                if (dp.size() == 0 || dp.get(dp.size() - 1) < num) {
                    dp.add(num);
                } else {
                	int index = binarySearch(dp, num);
                    dp.set(index, num); // index位置的元素换成num
                }  
            }
        }

        return n - dp.size();
    }

    public int binarySearch(List<Integer> list, int target) {
        int len = list.size();
        int left = 0, right = len - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            if (list.get(mid) < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 00:18:38  更:2021-07-28 00:19:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 15:39:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码