当前位置: 首页 > news >正文

实用指南:LeetCode算法日记 - Day 107: 最长重复子数组

目录

1. 最长重复子数组

1.1 题目解析

1.2 解法

1.3 代码实现


1. 最长重复子数组

https://leetcode.cn/problems/maximum-length-of-repeated-subarray/description/

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 

示例 1:

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。

示例 2:

输入:nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0]
输出:5

提示:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 100


1.1 题目解析

题目本质

  • 找 nums1 和 nums2 中 连续片段(子数组) 的最大公共长度。

  • 关键词:连续公共子数组最大长度

  • 本质可以理解为:“在两个序列中,找一段最长的完全一样的连续区间”。

常规解法

  • 枚举 nums1 中的起点 i

  • 枚举 nums2 中的起点 j

  • 从 i, j 开始往后一个个比,下去统计当前公共连续长度

  • 取所有起点组合中的最大值

// 暴力解法:枚举起点 + 向后比对
public int findLengthBruteForce(int[] nums1, int[] nums2) {int n = nums1.length;int m = nums2.length;int ans = 0;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {int k = 0;while (i + k < n && j + k < m && nums1[i + k] == nums2[j + k]) {k++;}ans = Math.max(ans, k);}}return ans;
}

问题分析

  • 最坏情况下:

    • 外层 i 有 n 个位置

    • 中层 j 有 m 个位置

    • 内层 k 最坏比较到 O(min(n, m))

  • 复杂度约为 O(n * m * min(n, m)),当 n, m = 1000 时,量级接近 10^9,会超时。

  • 可以预感

    • 暴力每次“从头比较”,很多重复比较没有复用。

    • 想要更快,就要“记住前面比过的结果”,典型就是 动态规划

思路转折

  • 观察:如果 nums1[i-1] == nums2[j-1],那么以这两个位置为结尾的公共子数组长度 = 以前一个位置为结尾的公共子数组长度 + 1。

  • 也就是说,当前结果依赖于“左上方”结果,是标准动态规划形态。

  • 于是我们考虑定义:dp[i][j] = nums1[0..i-1] 和 nums2[0..j-1] 的最长公共后缀长度

  • 则有递推:

    • 若 nums1[i-1] == nums2[j-1]:dp[i][j] = dp[i-1][j-1] + 1

    • 否则:dp[i][j] = 0(因为必须连续)

  • 这样每个状态只做 O(1) 工作,总状态数 O(n*m),复杂度刚好能压到 10^6 级别,完全可行。

1.2 解法

算法思想

使用二维动态规划

  • dp[i][j] 表示以 nums1[i-1]、nums2[j-1] 为 结尾 的最长公共子数组长度。

  • 递推公式: dp[i][j] = (nums1[i-1] == nums2[j-1]) ? dp[i-1][j-1] + 1 : 0

  • 扫描过程中维护全局最大值 ans。

i)取 n = nums1.length,m = nums2.length。

ii)申请 int[][] dp = new int[n + 1][m + 1];

  • 多一行一列,用作“哨兵行/列”,避免 i-1、j-1 越界。  

iii)外层循环 i 从 1 到 n

iv)内层循环 j 从 1 到 m

  • 若 nums1[i-1] == nums2[j-1]:

    • dp[i][j] = dp[i-1][j-1] + 1

    • 同时用 dp[i][j] 更新 ans。

  • 否则

    • dp[i][j] = 0(这里可以省略,因为数组默认 0,但写出来更清晰)。

v)两层循环结束后返回 ans 即为答案。

易错点

  • 下标错位:dp 用的是 1 开始,nums 是 0 开始,内层比较时一定要用 nums1[i-1]、nums2[j-1]。

  • 状态含义搞错:dp[i][j] 是“以 i-1、j-1 为结尾的公共后缀长度”,不是前缀长度,也不是从头开始。

  • 写成最长公共子序列(LCS)思路:LCS 的转移是 max(dp[i-1][j], dp[i][j-1]),会允许不连续,这道题不行,一旦不等就必须断开归零。

1.3 代码实现

class Solution {public int findLength(int[] nums1, int[] nums2) {int n = nums1.length;int m = nums2.length;// dp[i][j] 表示以 nums1[i-1] 和 nums2[j-1] 结尾的最长公共子数组长度int[][] dp = new int[n + 1][m + 1];int ans = 0;for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (nums1[i - 1] == nums2[j - 1]) {dp[i][j] = dp[i - 1][j - 1] + 1;if (dp[i][j] > ans) {ans = dp[i][j];  // 维护全局最大值}} else {dp[i][j] = 0; // 写上更直观(其实默认就是 0)}}}return ans;}
}

复杂度分析

  • 时间复杂度: O(n * m),两重循环,共 n * m 个状态,每个状态 O(1) 转移。

  • 空间复杂度:O(n * m),使用了一个 (n + 1) * (m + 1) 的二维数组

http://www.gsyq.cn/news/123030.html

相关文章:

  • 【Agent】MemOS 源码笔记---(6)---MemScheduler -- 总体
  • 三菱PLC与组态王打造饮料自动装箱机控制系统
  • 【Nature Communications‘24‘06】预训练多模态大语言模型经过 SkinGPT-4 提升皮肤病学诊断能力
  • 品牌营销战略策划公司选哪家靠谱?奇正沐古 - 资讯焦点
  • 幻方的 “已知” 与 “未知”:三阶唯一解、多阶构造及未解之谜
  • 宪法守护童年:向霸凌和诈骗说“不” - 资讯焦点
  • Neo4j启动
  • 2025年郑州头部吊顶式空调机组设计多少钱,空气幕/表冷器/卧式暗装风机盘管/吊顶式空调机组/工业暖风机吊顶式空调机组采购找哪家 - 品牌推荐师
  • 2025年嘉兴排行前列的卧式暗装风机盘管采购多少钱,卡式风机盘管/吊顶式空调机组/空气幕/消防排烟防火阀卧式暗装风机盘管采购怎么选择 - 品牌推荐师
  • 无代码解决方案:解锁数字化转型的普惠路径
  • Oracle索引技术:理论与实操全解析
  • 深入理解Golang并发模型与CSP理论
  • 23、Samba使用与SSL配置全解析
  • 人工智能如何改变 Anthropic 的工作方式
  • 代码之恋(第十四篇:分叉的路径与意外的Push)
  • SpringSecurity授权原理与实战
  • 告别API碎片化!用AI Ping获取MiniMax-M2、GLM-4.6与Kimi-K2
  • 100G双光口网卡技术解析:Intel E810-CAM2方案的性能与应用突破
  • 2025.12.18博客
  • 刚刚,Gemini 3 Flash 正式上线!位置稳居第一!
  • 2025年杨浦服务好的宠物医院哪家靠谱推荐,母狗绝育/猫咪绝育/狗狗绝育/宠物绝育/宠物体检/宠物内科/宠物皮肤科/宠物医院宠物医院最好的 - 品牌推荐师
  • 构筑测试事业的北极星——软件测试愿景制定指南
  • 基于红外图像的弹道导弹弹道段轨迹估计
  • 从“幻觉”到“诚实”:OpenAI 如何重新定义大模型的不靠谱问题
  • 34、UNIX 中 vi 编辑器的多场景应用与多文件编辑技巧
  • 递归加料——回溯算法
  • NX UG 12.0 安装教程:安全获取 + 避坑指南,零基础也能搞定
  • 9 个降AI率工具,继续教育学生必备!
  • 谢飞机的面试之旅:如何在互联网大厂面试中脱颖而出
  • 操作系统与数据结构核心知识点解析