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;
    }
}
posted @ 2021-11-05 16:49  rananie  阅读(31)  评论(0编辑  收藏  举报