给两个整数数组 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
>>思路和分析
用一个二维的矩阵,也就是二维的dp数组,表示这两个数组比较的所有状态。其实本题相对来说就简单很多了,因为后面的递推公式,遍历顺序,初始化,都比较简单,关键就是在于如何用dp数组去把这两个数组的比较情况,把状态保存出来。这一点是本题的难点所在。
>>动规五部曲
1.确定 dp数组 以及下标含义
2.确定递推公式

3.dp数组初始化
根据 dp[i][j] 的定义,可知 dp[i][0] 和 dp[0][j] 都是没有意义的!但 dp[i][0] 和 dp[0][j] 要初始值,为了方便递推公式dp[i][j] = dp[i-1][j-1] + 1;那么可将 dp[i][0] 和 dp[0][j] 初始化为0
4.确定遍历顺序 (这两种遍历方式都可以!)
5.举例推导 dp 数组

- class Solution {
- public:
- // 动态规划
- // 时间复杂度:O(n x m),n为nums1长度,m为nums2长度
- // 空间复杂度:O(n x m)
- int findLength(vector<int>& nums1, vector<int>& nums2) {
- vector
int>> dp(nums1.size()+1,vector<int>(nums2.size()+1,0)); - int result=0;
- for(int i=1;i<=nums1.size();i++) {
- for(int j=1;j<=nums2.size();j++) {
- if(nums1[i-1] == nums2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
- if (dp[i][j] > result) result = dp[i][j];
- }
- }
- return result;
- }
- };
>>优化空间复杂度「滚动数组」
注意事项

- class Solution {
- public:
- // 优化 + 滚动数组
- int findLength(vector<int>& nums1, vector<int>& nums2) {
- vector<int> dp(vector<int>(nums2.size()+1,0));
- int result=0;
- for(int i=1;i<=nums1.size();i++) {
- for(int j=nums2.size();j>0;j--) {
- if(nums1[i-1] == nums2[j-1]) dp[j] = dp[j-1] + 1;
- else dp[j]=0;// 注意这里不相等的时候要有赋0的操作
- if (dp[j] > result) result = dp[j];
- }
- }
- return result;
- }
- };
拓展:若我想定义dp[i][j] 是以下标 i 为结尾的nums1,以下标 j 为结尾的 nums2 的最长重复子数组长度,可行不?
可行,只是实现相对麻烦一些。需要将第一行和第一列进行初始化

注意事项:为了让 if (dp[i][j] > result) result = dp[i][j]; 收集到全部结果,两层for训练一定从0开始遍历,这样需要加上 && i > 0 && j > 0 的判断

- class Solution {
- public:
- int findLength(vector<int>& nums1, vector<int>& nums2) {
- vector
int>> dp (nums1.size() + 1, vector<int>(nums2.size() + 1, 0)); - int result = 0;
-
- // 要对第一行,第一列经行初始化
- for (int i = 0; i < nums1.size(); i++) if (nums1[i] == nums2[0]) dp[i][0] = 1;
- for (int j = 0; j < nums2.size(); j++) if (nums1[0] == nums2[j]) dp[0][j] = 1;
-
- for (int i = 0; i < nums1.size(); i++) {
- for (int j = 0; j < nums2.size(); j++) {
- if (nums1[i] == nums2[j] && i > 0 && j > 0) { // 防止 i-1 出现负数
- dp[i][j] = dp[i - 1][j - 1] + 1;
- }
- if (dp[i][j] > result) result = dp[i][j];
- }
- }
- return result;
- }
- };
总结:我们可以发现方案二其实是在方案一的二维dp数组的上外围和左外围多加了一层0包裹,这样做的好处是可以统一操作,简化代码,也可以更加方便的利用滚动数组进行状态压缩
- 方案一:
- if (nums1[i] == nums2[j] && i > 0 && j > 0) { // 防止 i-1 出现负数
- dp[i][j] = dp[i - 1][j - 1] + 1;
- }
-
- 方案二:
- if(nums1[i-1] == nums2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
参考和推荐文章、视频:
动态规划之子序列问题,想清楚DP数组的定义 | LeetCode:718.最长重复子数组_哔哩哔哩_bilibili
来自代码随想录课堂截图:
