718.最长重复子数组
718.最长重复子数组
题目
给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
示例:
输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出:3
解释:
长度最长的公共子数组是 [3, 2, 1] 。
提示:
1 <= len(A), len(B) <= 1000
0 <= A[i], B[i] < 100
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
- 多阶段决策
- 不问具体解,只求最优解
这道题可以使用 动态规划 。
dp[i]表示以A[i]结尾的数组和B数组中公共的、长度最长的子数组长度。
最开始是这样定义的,然后发现做不出来,B的数字可以重复的,B数组也需要确定下来。
dp[i][j]表示以A[i-1]结尾的和B[j-1]结尾的数组中公共的、长度最长的子数组长度。
dp[i][j]如何推导?
- 如果A[i-1]==B[j-1],dp[i][j] = dp[i-1][j-1] + 1;
- 如果A[i-1]≠B[i-1], dp[i][j] = 0;
dp数组初始化
状态定义以A[i-1]结尾也是为了方便初始化,如果定义为A[i]结尾,那么当i=0时与j=0时都需要初始化,不能直接在循环里面计算。
dp[0][0]在这里是没有意义的。
i=0或者j=0,dp值都为0
int result = 0;
dp[1][1]=(A[0]==B[0])?1:0;
for(int i=1;i<=A.length;i++){
for(int j=1;j<=B.length;j++){
if(A[i-1]==B[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(int[] nums1, int[] nums2) {
int lenA=nums1.length;
int lenB=nums2.length;
int dp [][] = new int [lenA+1] [lenB+1];
int result = 0;
for(int i=1;i<=lenA;i++){
for(int j=1;j<=lenB;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;
}
}
滚动数组
把上一层dp[i - 1][j]拷贝到下一层dp[i][j]来继续用。
此时遍历B数组的时候,就要从后向前遍历,因为dp[i][j]需要的是
dp[i-1][j-1]的值,如果正向遍历,dp[j-1]代表的是dp[i][j-1]的值。倒序遍历还需要注意的是,如果不相等需要赋值0,因为正序最后一个元素可以依靠前面的元素递推,倒序的话需要手动赋值。
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int lenA=nums1.length;
int lenB=nums2.length;
int dp [] = new int [lenB+1];
int result = 0;
for(int i=1;i<=lenA;i++){
for(int j=lenB;j>=1;j--){
if(nums1[i-1]==nums2[j-1])dp[j] = dp[j-1] + 1;
else dp[j]=0;
if(dp[j]>result) result =dp [j];
}
}
return result;
}
}