力扣-718最长重复子数组
1、题目
给两个整数数组 nums1
和 nums2
,返回 两个数组中 公共的 、长度最长的子数组的长度 。
示例 1:
输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。
2、分析
-
题目。由题目可知,我们要求得是两个数组中间最长的相同的一部分,这里我还是首先想到的是暴力,推一下确实暴力也能够进行推出来。但是我们要进一步看能不能进行优化,根据最长长度,我们可以联想到是不是可以使用dp动态规划,答案是可以的。
-
1)二维dp推导。这里我们要推导,所以跟前一个状态有关,前一个状态是什么?那就是nums1[i - 1]和nums2[j - 1]相等,也就是前一个数是相等的,所以我们到当前这个数时就做长度+1操作。也就是dp[i]【j】=dp[i - 1]【j-1】+1。
2)或者第二种想法,就是按图形来分析。两个数组,一个为列,一个为行,所以就对应的话,这个对应的十字交点相等,那么值就是左上角值+1,也就是dp[i]【j】=dp[i - 1]【j-1】+1。
-
dp边界。由于第一个数可能会相等,所以左上角必须有值也就是i-1与j-1得,所以长度要比nums数组多1。
-
一维dp推导,由图像可知,后一行的数是前一行的数复制到下一行然后如果当前数前一个相等,就当前数为前面一个加1。也就是至于那一行的数有关,dp[j]=dp[j-1] + 1;
3、代码及注释
class Solution {
public int findLength(int[] nums1, int[] nums2) {
// 1.进行暴力遍历
int max = 0;
for (int i = 0; i < nums1.length; i++){
int temp = 0;
for (int j = 0; j < nums2.length; j++) {
if (nums1[i] == nums2[j]){
temp++;
int k = i;
int m = j;
while(k + 1 < nums1.length && m + 1 < nums2.length && nums1[++k] == nums2[++m]){
temp++;
}
max = Math.max(max, temp);
temp = 0;
}
}
}
return max;
// 2.二维dp
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
int max = 0;
for (int i = 1; i < nums1.length + 1; i++){
for (int j = 1; j < nums2.length + 1; j++){
if (nums1[i - 1] == nums2[j - 1]){
dp[i][j] = dp[i - 1][j - 1] + 1;
max = Math.max(max, dp[i][j]);
}
}
}
return max;
// 3.一维dp
int[] dp = new int[nums2.length + 1];
int max = 0;
for (int i = 1; i < nums1.length + 1; i++){
for (int j = nums2.length; j > 0; j--){
if (nums1[i - 1] == nums2[j - 1]){
dp[j] = dp[j - 1] + 1;
}else{
// 每一次继承上一行的dp[j]值,如果相等,就会+1存max中,如果不相等,当前行刷新dp[j]值为0。
dp[j] = 0;
}
max = max > dp[j] ? max : dp[j];
}
}
return max;
}
}
4、练习
力扣题目练习:718. 最长重复子数组