LogicStack-LeetCode 题解精读:1713. 得到子序列的最少操作次数——LCS 转 LIS 与「贪心 + 二分」的完整证明
教程文档【免费下载链接】LogicStack-LeetCode公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码项目地址https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode点击查看免费下载本文是「宫水三叶的刷题日记」仓库中 LeetCode 1713 题解的技术深度解读。题目要求通过任意位置插入整数使target成为arr的子序列核心难点在于数据规模达 10^5 时朴素 LCS 的 O(n×m) 解法必然超时。文章将完整继承原题解的分析脉络先抽象成最长公共子序列LCS问题再利用「target元素互不相同」这一关键限制将 LCS 转换为最长上升子序列LIS最后用「维护单调序列 二分」的贪心解法把整体复杂度降到 O(n m log m)。读完你将同时掌握两类经典序列问题的互相转化技巧以及 LIS 贪心解法的严格正确性证明。题目描述与示例给定两个数组target包含若干互不相同的整数arr可能包含重复元素。每一次操作中可以在arr的任意位置包括开头与末尾插入任一整数。要求返回最少操作次数使得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^51 target[i], arr[i] 10^9target不包含任何重复元素基本分析最少操作次数与 LCS 的关系令target长度为 narr长度为 m二者**最长公共子序列LCS**长度为max。由于我们只能在arr中插入元素、不能删除元素target成为arr子序列的充要条件是target中不在arr中按顺序出现的那部分元素需要被插入补齐。换句话说已经免费存在的部分就是target与arr的最长公共子序列剩余 n - max 个元素必须手动插入。因此最终答案为n - max。从题面看这是一道标准的 LCS 问题朴素求解需要定义状态f[i][j]为「考虑a数组前 i 个元素和b数组前 j 个元素的最长公共子序列长度」复杂度 O(n×m)。本题数据范围达 10^5朴素做法必然超时。一个非常显眼的切入点是target数组元素各不相同。当 LCS 问题增加其中一个数组元素互不相同的条件限制后会存在一个经典且优美的性质当其中一个数组元素各不相同时LCS 问题可以转换为 LIS 问题求解而 LIS 问题存在「维护单调序列 二分」的贪心解法复杂度为 O(n log n)。因此本题的解题路径是抽象成 LCS 问题利用target元素互不相同转换为 LIS 问题使用 LIS 的贪心解法单调数组 二分做到 O(n m log m)。下面分别严格证明第 2 步和第 3 步的合理性与正确性。核心洞察LCS 与 LIS 的一一对应关系本质结论当其中一个数组元素各不相同时每一个公共子序列都一一对应着不重复元素数组的下标数组中的某个上升子序列反之亦然。以本题的target和arr为例由于target元素各不相同target元素与其下标之间存在唯一映射关系将注意力集中在两数组的公共元素上忽略非公共元素把arr中的公共元素替换为它在target中的下标此时原 LCS 问题等价于在这个下标数组中找最长上升子序列LIS。注意示意图只画出了两个数组的某个片段不要错误理解为两数组等长原题解配图可参见仓库文档 1713. 得到子序列的最少操作次数困难。正向对应如果存在某个公共子序列根据子序列定义它在target中的对应下标序列必然严格递增即对应一个上升子序列。反向对应对于下标数组的某个上升子序列其每个元素都意味着该值在target中出现过且出现顺序递增完全符合公共子序列定义即对应一个公共子序列。由此原问题从 LCS 严格转换为了 LIS转换本身不丢失任何信息。LIS 贪心解法的正确性证明朴素 LIS 与贪心 LIS 的两个数组朴素 LIS 需要定义动规数组ff[i]代表以nums[i]为结尾的最长上升子序列长度。计算f[i]时需要回看[0, i-1]区间内所有满足nums[j] nums[i]的位置 j取所有f[j] 1的最大值因此朴素 LIS 是 O(n²)。贪心解法则额外维护一个数组gf动规数组与朴素解法含义一致f[i]代表以nums[i]为结尾的上升子序列的最大长度g贪心数组g[len] x代表长度为 len 的上升子序列的「最小结尾元素」为 x。我们期望用g数组代替线性遍历计算f[i]时需要找到满足g[idx] nums[i]的最大下标idx即最后一个还能接上nums[i]的位置。如果g数组单调递增就能通过二分在 O(log n) 内找到这个分割点。反证法证明 g 数组单调递增假设存在位置 i j 使得g不满足单调递增只有两种可能情况一g[i] g[j] x这意味着某个值 x 既能作为长度 i 的上升子序列的最后一位也能作为长度 j 的上升子序列的最后一位。根据定义g[i] x是所有长度为 i 的上升子序列中的最小结尾。但由于g[j] x且上升子序列必然严格单调我们可以删除长度为 j 的子序列末尾若干元素构造出一个长度为 i 的子序列得到一个新的长度为 i 的上升子序列其结尾元素严格小于 x这与x是长度为 i 的最小结尾矛盾。故g[i] g[j]恒不成立。情况二g[i] g[j] x同理若存在长度为 j 的合法上升子序列、其最小结尾为 x那么删掉末尾元素必然能构造出长度为 i 的上升子序列其结尾小于 x从而可以更新g[i]为更小的值与g[i] x矛盾。故g[i] g[j]恒不成立。根据全序关系排除了相等与大于两种可能后只能有g[i] g[j]恒成立即g数组严格单调递增。由于每个f[i]的取值在贪心做法与朴素做法中完全一致转移来源通过单调的g数组二分确定贪心解法正确性得证。动态规划 贪心 二分完整实现算法步骤建立下标映射用哈希表map记录target中每个元素的下标O(n)构造下标数组遍历arr只保留出现在target中的元素替换为其下标得到数组listO(m)。list的长度len m贪心求 LIS维护动规数组f与贪心数组gg初始化为极大值代表长度为 0 的结尾不存在对list中每个元素在g上二分找到满足g[mid] list[i]的最大下标mid得到clen mid 1更新f[i] clen用g[clen] min(g[clen], list[i])维护最小结尾元素用max max(max, clen)记录全局最长上升子序列长度输出答案n - max。边界情况若两数组无公共元素list为空max 0答案为 n全部元素都需要插入若arr已按顺序完整包含targetmax n答案为 0。注意元素值可达 10^9必须使用哈希表HashMap/map/字典而非按值开数组。Java 实现class Solution { public int minOperations(int[] t, int[] arr) { int n t.length, m arr.length; MapInteger, Integer map new HashMap(); for (int i 0; i n; i) { map.put(t[i], i); // target 元素 - 下标唯一映射 } ListInteger list new ArrayList(); for (int i 0; i m; i) { int x arr[i]; if (map.containsKey(x)) list.add(map.get(x)); // 只保留公共元素的下标 } int len list.size(); int[] f new int[len], g new int[len 1]; Arrays.fill(g, Integer.MAX_VALUE); // g[0] 保持极大值保证 clen 至少为 1 int max 0; for (int i 0; i len; i) { int l 0, r len; // 二分查找最后一个 g[mid] list[i] 的位置 while (l r) { int mid l r 1 1; // 取上中位数避免死循环 if (g[mid] list.get(i)) l mid; else r mid - 1; } int clen r 1; // 以 list[i] 结尾的最长上升子序列长度 f[i] clen; g[clen] Math.min(g[clen], list.get(i)); // 维护“最小结尾元素” max Math.max(max, clen); } return n - max; } }C 实现class Solution { public: int minOperations(vectorint t, vectorint arr) { int n t.size(), m arr.size(); mapint, int map; for (int i 0; i n; i) { map[t[i]] i; } vectorint list; for (int i 0; i m; i) { int x arr[i]; if (map.count(x)) list.push_back(map[x]); } int len list.size(); vectorint f(len), g(len 1, numeric_limitsint::max()); int maxVal 0; for (int i 0; i len; i) { int l 0, r len; while (l r) { int mid l r 1 1; if (g[mid] list[i]) l mid; else r mid - 1; } int clen r 1; f[i] clen; g[clen] min(g[clen], list[i]); maxVal max(maxVal, clen); } return n - maxVal; } };Python 实现说明仓库原文档中 Python 与 TypeScript 代码块误粘贴了 C 片段属于文档编辑时的复制粘贴痕迹此处按原题解算法给出可运行的对应语言版本。class Solution: def minOperations(self, t: List[int], arr: List[int]) - int: n, m len(t), len(arr) map_ {t[i]: i for i in range(n)} # target 元素 - 下标 list_ [map_[x] for x in arr if x in map_] # 只保留公共元素的下标 sz len(list_) f, g [0] * sz, [float(inf)] * (sz 1) # g 用正无穷初始化 maxv 0 for i in range(sz): l, r 0, sz while l r: mid l r 1 1 if g[mid] list_[i]: l mid else: r mid - 1 clen r 1 f[i] clen g[clen] min(g[clen], list_[i]) maxv max(maxv, clen) return n - maxvTypeScript 实现function minOperations(t: number[], arr: number[]): number { const n t.length, m arr.length; const map new Mapnumber, number(); for (let i 0; i n; i) map.set(t[i], i); const list: number[] []; for (let i 0; i m; i) { const x arr[i]; if (map.has(x)) list.push(map.get(x)!); } const sz list.length; const f new Array(sz).fill(0); const g new Array(sz 1).fill(Number.MAX_SAFE_INTEGER); let maxv 0; for (let i 0; i sz; i) { let l 0, r sz; while (l r) { const mid (l r 1) 1; if (g[mid] list[i]) l mid; else r mid - 1; } const clen r 1; f[i] clen; g[clen] Math.min(g[clen], list[i]); maxv Math.max(maxv, clen); } return n - maxv; }复杂度分析时间复杂度O(n) 建立target下标映射O(m) 构造映射数组list贪心求解 LIS 时每个元素一次二分为 O(len·log len) ≤ O(m·log m)。整体O(n m log m)空间复杂度映射表 O(n)list、f、g数组 O(m)整体O(n m)。对比朴素 LCS 的 O(n×m)本题通过元素互不相同 → LCS 转 LIS → 贪心二分把复杂度降到了接近线性的水平这是数据范围 10^5 下能够通过的关键。举一反三仓库中的关联题目本题串联了「最长公共子序列」「最长上升子序列」「贪心」「二分」四类知识点仓库中与之配套的题解可以按图索骥继续深入LCS 朴素解法与状态定义1143. 最长公共子序列中等——标准的二维 DPf[i][j]转移方程为本题基本分析的起点LCS 变体1092. 最短公共超序列困难LIS 的排序 一维化技巧354. 俄罗斯套娃信封问题困难——同为困难题通过排序把二维问题化为 LIS可以对照学习降维思路LIS 的朴素序列 DP 与方案数统计673. 最长递增子序列的个数中等——在f数组基础上额外维护g计数数组可加深对动规数组含义的理解LIS 特例334. 递增的三元子序列中等知识目录本题已被收录进 Index/序列 DP.md推荐指数 与 Index/二分.md 的分类索引中可在仓库 Index 目录下按 Tag 检索同类型题目。总结回顾整道题的思维链路最少插入次数 n - 最长公共子序列长度是第一步抽象元素互不相同 → 公共子序列与下标上升子序列一一对应是问题转化的钥匙g 数组单调递增反证法 二分定位转移是复杂度跃迁的引擎。最终以 O(n m log m) 的复杂度解决 10^5 级别的数据完整代码与逐条证明均可在仓库文档 1713. 得到子序列的最少操作次数困难 中找到对应实现。赞分享教程文档【免费下载链接】LogicStack-LeetCode公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码项目地址https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode点击查看免费下载相关推荐LeetCode 1713 得到子序列的最少操作次数用 LIS 换皮题拆解「索引映射 贪心二分」套路LeetCode 1713 得到子序列的最少操作次数用 LIS 换皮题拆解「索引映射 贪心二分」套路 本篇题解以 leetcode 解题仓库中 1713.文档教程知识库LeetCode最长子序列完全指南LIS与LCS问题全集解析LeetCode最长子序列完全指南LIS与LCS问题全集解析 在算法世界中子序列问题一直是面试和笔试的热门考点而最长递增子序列LIS和最长公共子序列文档教程知识库LogicStack-LeetCode 题解精讲LeetCode 1403 非递增顺序的最小子序列——排序 贪心取最大元素LogicStack LeetCode 题解精讲LeetCode 1403 非递增顺序的最小子序列——排序 贪心取最大元素 本文导读 本文以《LogicS教程文档上一篇Bruno彻底告别API测试烦恼的开源神器下一篇革命性Docker部署体验Dokploy v0.19.0带来组织隔离与智能部署新范式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

【高级】系统架构师 | 2025年上半年综合真题DAY2

【高级】系统架构师 | 2025年上半年综合真题DAY2

11.大模型生成代码的核心是(A) A.Transformer B.Rule Base System C.Data Flow D.Knowledge Map 解析: 当前主流的大模型(如 GPT 系列、LLaMA等)生成代码的核心架构是Transformer。Transformer 基于自注意力机制,能够捕捉文本序列中的长距离依赖关系,非常适合处理代码这种具…

2026/10/9 1:26:59 阅读更多 →
Strata Coder 实测:12 GB 显卡 + 32 GB 内存长上下文性能、专家缓存命中与实验加速投影

Strata Coder 实测:12 GB 显卡 + 32 GB 内存长上下文性能、专家缓存命中与实验加速投影

人工智能大模型本地部署推理引擎模型推理服务模型量化 【免费下载链接】Strata Qwen3.8-Flash-Next on any consumer hardware: one-click install for Windows / Linux. Strata inference engine, OpenAI/Anthropic API on localhost, optional image input. 项目地址&#xf…

2026/10/9 1:26:59 阅读更多 →
题解:洛谷 P9124 [USACO23FEB] Bakery S(废)

题解:洛谷 P9124 [USACO23FEB] Bakery S(废)

本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。 欢迎大家订阅我的专栏:算法…

2026/10/9 1:26:59 阅读更多 →

最新新闻

双指针算法全攻略:对撞、快慢、滑动窗口三大模板与实战总结

双指针算法全攻略:对撞、快慢、滑动窗口三大模板与实战总结

刷题刷到一定量,很多人会慢慢总结出一条规律:有一类题的解法特别“固定”——有序数组里找两个数凑目标值、链表中判断有没有环、字符串里找不重复的最长子串,题面长得完全不一样,翻开题解一看,底层全是同一个思路&…

2026/10/9 3:32:13 阅读更多 →
医疗NLP实战:词典构建与最大匹配实体标注

医疗NLP实战:词典构建与最大匹配实体标注

简介:一套基于Python与Jupyter构建的医疗实体识别模型资源,面向疾病、症状、身体部位三类实体,完整呈现词典构造、语料标注、模型训练与结果评估的工程化流程。压缩包共147个文件,约581MB,具体包含18个txt词典/文本、1…

2026/10/9 3:32:13 阅读更多 →
Git远程分支覆盖本地分支:reset、clean实操与急救指南

Git远程分支覆盖本地分支:reset、clean实操与急救指南

1. 什么时候需要“用远程分支覆盖本地分支”先聊个真实的场景。我在维护一个项目时,远程仓库里develop分支已经被同事 rebase 重新整理过,提交历史完全换了样子。我本地还停在老版本上,这时候直接git pull会提示分叉严重,甚至直接…

2026/10/9 3:32:13 阅读更多 →
Cache模拟器实战:从映射原理到命中率计算的完整工程解析

Cache模拟器实战:从映射原理到命中率计算的完整工程解析

简介:一份面向计算机组成原理与操作系统学习者的缓存模拟器源码,在Visual Studio 2010环境下编写,通过读取地址流文件模拟处理器访存行为,可设置缓存容量、块大小,并支持直接映射、组关联映射、全关联映射三种策略&…

2026/10/9 3:32:13 阅读更多 →
Servlet配置实战:web.xml与@WebServlet注解全面解析

Servlet配置实战:web.xml与@WebServlet注解全面解析

Servlet这个词,放在今天动辄微服务、云原生的大环境下,多少有点“老古董”的感觉。但你只要还在写Java后端,不管用Spring Boot还是Spring MVC,请求真正进来之后,最终处理的还是Servlet容器那一层。很多新人会直接跳过S…

2026/10/9 3:32:13 阅读更多 →
Claude Code与桌面版安装教程:环境配置、VS Code插件及MCP部署

Claude Code与桌面版安装教程:环境配置、VS Code插件及MCP部署

最近一直被同一个问题刷屏:“Claude到底怎么装?”尤其是Claude Code这三个月火起来之后,各大群里问安装的比问用法的还多。我前前后后帮朋友远程装过几十次,也踩了不少坑——什么安装到一半卡死、装完打开白屏、输入命令提示找不到…

2026/10/9 3:31:13 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 13:34:55 阅读更多 →