news 2026/8/14 16:24:06

动态规划专练:力扣第718、1143题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态规划专练:力扣第718、1143题

力扣第718题-最长重复子数组

1.本题是一道考察动态规划的经典问题,设置一个二维dp[nums1Size + 1][nums2Size + 1]数组,来记录长度为i的nums1和长度为j的nums2的最长公共子数组长度,元素初始化为0。当nums1[i - 1] == nums2[j - 1]时说明当前元素相同,此时的最长公共子数组长度dp[i][j]就等于dp[i - 1][j - 1] + 1。每次循环都更新当前最长的公共子数组长度res。完整代码如下:

1. int findLength(int* nums1, int nums1Size, int* nums2, int nums2Size) { 2. // dp[i][j]:nums1前i个、nums2前j个元素,以nums1[i-1]、nums2[j-1]结尾的最长公共子数组长度 3. int dp[nums1Size + 1][nums2Size + 1]; 4. // 初始化dp数组全部置0 5. for (int i = 0; i <= nums1Size; i++){ 6. memset(dp[i], 0, sizeof(dp[i])); 7. } 8. 9. int res = 0; 10. // 遍历nums1每一位 11. for (int i = 1; i <= nums1Size; i++){ 12. // 遍历nums2每一位 13. for (int j = 1; j <= nums2Size; j++){ 14. // 当前两数字相等,公共子数组长度 = 左上角dp值 + 1 15. if (nums1[i - 1] == nums2[j - 1]){ 16. dp[i][j] = dp[i - 1][j - 1] + 1; 17. } 18. // 不相等时dp[i][j]保持0,更新全局最大长度 19. res = fmax(res, dp[i][j]); 20. } 21. } 22. 23. return res; 24. }

该算法时间复杂度和空间复杂度均为O(nums1Size * nums2Size)。

2.可以看到递推公式中当前项的dp只和上一层的有关,所以可以将二维dp数组改为一维动态dp数组。需要注意的是此时的内层循环就需要逆序遍历,防止元素被重复计算,同时当nums1[i - 1] != nums2[j - 1]时说明连续子数组在这里断掉了,需要将当前dp值置零。完整代码如下:

1. int findLength(int* nums1, int nums1Size, int* nums2, int nums2Size) { 2. // 一维滚动dp数组,dp[j]表示nums1前i个、nums2前j个以末尾元素结尾的最长公共子数组长度 3. int dp[nums2Size + 1]; 4. // 数组初始化为0 5. memset(dp, 0, sizeof(dp)); 6. 7. int res = 0; 8. // 遍历nums1每一个元素 9. for (int i = 1; i <= nums1Size; i++){ 10. // 倒序遍历nums2,防止dp[j-1]提前被覆盖 11. for (int j = nums2Size; j >= 1; j--){ 12. if (nums1[i - 1] == nums2[j - 1]){ 13. // 当前元素匹配,继承左上方dp[j-1]的值并+1 14. dp[j] = dp[j - 1] + 1; 15. } else { 16. // 元素不匹配,以当前位置结尾的公共子数组长度归零 17. dp[j] = 0; 18. } 19. // 更新全局最长公共子数组长度 20. res = fmax(res, dp[j]); 21. } 22. } 23. 24. return res; 25. }

该算法时间复杂度为O(nums1Size * nums2Size),空间复杂度为O(nums2Size)。

力扣第1143题-最长公共子序列

1.本题和力扣第718题-最长重复子数组比较相似,区别在于本题的公共子序列不要求连续,这就代表最长公共子序列的值在dp数组中可以继承而不是清零。当text1[i - 1] == text2[j - 1]时递推公式仍为dp[i][j] = dp[i - 1][j - 1] + 1,而不相等时就要比较上方或者左边的较大值来继承(从这两个方向前进一步都可以到达当前位置,所以有两种情况),递推公式为dp[i][j] = fmax(dp[i - 1][j], dp[i][j - 1])。完整代码如下:

1. int longestCommonSubsequence(char* text1, char* text2) { 2. // 获取两个字符串长度 3. int len1 = strlen(text1); 4. int len2 = strlen(text2); 5. // dp[i][j]:text1前i个字符、text2前j个字符的最长公共子序列长度 6. int dp[len1 + 1][len2 + 1]; 7. // 将dp数组全部初始化为0 8. for (int i = 0; i <= len1; i++){ 9. memset(dp[i], 0, sizeof(dp[i])); 10. } 11. 12. // 遍历text1每个字符 13. for (int i = 1; i <= len1; i++){ 14. // 遍历text2每个字符 15. for (int j = 1; j <= len2; j++){ 16. if (text1[i - 1] == text2[j - 1]){ 17. // 字符相等,公共子序列长度等于左上角值+1 18. dp[i][j] = dp[i - 1][j - 1] + 1; 19. } else { 20. // 字符不等,取上方或左方较大值 21. dp[i][j] = fmax(dp[i - 1][j], dp[i][j - 1]); 22. } 23. } 24. } 25. 26. // 两字符串全部字符对应的最长公共子序列结果 27. return dp[len1][len2]; 28. }

该算法时间复杂度和空间复杂度均为O(len1 * len2)。

2.本题也可以使用一维动态dp数组,内层循环由于在字符不等的情况下必须比较同行左边的和上一次当前位置的值,所以dp[j - 1]需要使用已经更新后的值,必须使用正序遍历。同时为了避免元素被重复使用,需要一个记录之前元素的变量pre和一个记录当前元素的变量cur来辅助(之前都是通过逆序来解决)。完整代码如下:

1. int longestCommonSubsequence(char* text1, char* text2) { 2. int len1 = strlen(text1); 3. int len2 = strlen(text2); 4. // 一维滚动dp数组,dp[j]代表text1前i个字符、text2前j个字符的LCS长度 5. int dp[len2 + 1]; 6. memset(dp, 0, sizeof(dp)); 7. 8. for (int i = 1; i <= len1; i++){ 9. // pre保存dp[j-1]更新前的值,等价二维dp[i-1][j-1] 10. int pre = dp[0]; 11. for (int j = 1; j <= len2; j++){ 12. // 记录更新前的dp[j],作为下一轮j+1的pre 13. int cur = dp[j]; 14. if (text1[i - 1] == text2[j - 1]){ 15. // 字符匹配,取左上角pre+1 16. dp[j] = pre + 1; 17. } else { 18. // 不匹配,取上方旧dp[j]或左侧新dp[j-1]最大值 19. dp[j] = fmax(dp[j], dp[j - 1]); 20. } 21. pre = cur; 22. } 23. } 24. 25. return dp[len2]; 26. }

该算法时间复杂度为O(len1 * len2),空间复杂度为O(len2)。

3.遍历方向由状态转移方程中最严苛的依赖限制唯一决定。只要推导分支中存在任何对当前行(新数据,如dp[i][j-1])的依赖,就强制要求正序遍历。在此强制正序的前提下,为解决同时需要上一行旧数据(如dp[i-1][j-1])造成的读写冲突,不改变遍历方向,而是通过引入标量缓存(即pre变量)进行空间置换。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/14 16:22:21

国产协同软件私有化替代:信创驱动的市场机会与挑战

国产协同软件私有化替代&#xff1a;信创政策驱动下的市场机会与实施挑战 一、信创倒计时下的国产协同软件私有化替代&#xff1a;一场被交付周期延误的数字化迁徙 信创政策的推进正在改写政务与国企的信息化采购逻辑。从政策红头文件到 CIO 办公桌上的倒排工期&#xff0c;OA …

作者头像 李华
网站建设 2026/8/14 16:19:36

不燃电解液锂金属循环差的真相:不是SEI持续分解,而是传输特性

TL;DR&#xff1a;ETH Zurich团队利用operando EQCM-D与operando FTIR双原位技术&#xff0c;直接否定了磷酸酯不燃电解液中"SEI持续分解导致循环差"的主流假设。 实验表明SEI在首次还原阶段即基本形成完成并快速钝化电极&#xff0c;真正制约锂金属可逆循环的是SEI的…

作者头像 李华
网站建设 2026/8/14 16:16:50

Python 调生图 API 的最小可用代码 + 并发改造

Python 怎么调 AI 生图 API&#xff1f;三十行跑通 nano banana pro&#xff08;含重试与异步并发&#xff09; 网上 Python 调图像生成接口的例子大多绑死某一家 SDK&#xff0c;换个服务就得重写。其实完全不用 SDK——该服务兼容 OpenAI 的 chat/completions 协议&#xff0…

作者头像 李华
网站建设 2026/8/14 16:14:33

GB/T 12459-2025 新国标完整技术解读|替代 2017 版,管道工程师必看

2025-10-31 发布、2026 年 5 月 1 日正式实施的 GB/T 12459-2025《钢制对焊管件 类型与参数》全面替代沿用 8 年的 GB/T 12459-2017&#xff0c;是石油化工、火电、长输燃气、核电、精细化工仪表管线的核心基础管件标准。 本次修订绝非简单文字修正&#xff0c;而是全维度技术…

作者头像 李华
网站建设 2026/8/14 16:12:32

MVP 接口要能演进,字段和错误语义先立约

MVP 接口要能演进&#xff0c;字段和错误语义先立约 MVP 追速度&#xff0c;不等于接口可以只靠口头约定。字段、错误语义和兼容规则越晚确定&#xff0c;客户端、后端与项目排期越容易被一次小改动同时拖住。 然而&#xff0c;当产品通过 PMF 验证进入规模化&#xff08;Scale…

作者头像 李华