LeetCode 718 最长重复子数组

853 阅读3分钟

最长公共子序列和最长公共子串都是dp的经典问题,是一定需要能随时写出来的。昨天大疆的笔试考到了,想到自己还没有刷到这题,因此今天就来分析总结一下这个问题的解法。leetcode上这题不是公共子串而是重复子数组,但是实际上是一样的。因为有leetcode方便测试代码,所以下面的代码是最长重复子数组的,换成串也一样。

1. 题目描述

给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。

示例 1:

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

2. 解题思路

这是一道经典的动态规划的题目,能用动态规划来解说明存在最优子问题。假设在A数组中的i位置和B数组的j位置往前长度为L的子数组是相同的,那么只要A中i+1的位置和B中j+1的位置的元素相同,那么重复子数组的长度就可以递增1。这就是这个问题能用dp解的理论基础。因此只需要从最简单的情况i=0和j=0的情况就可以向后递推得到任意i,j上的重复子数组的长度,最后只需要取出最长的即可。
递推形式确定了,那么dp算法就很好写了。只需要照着之前总结的dp写法写就很容易写出完整的代码。
照着dp的写法步骤代码如下:

class Solution {
public:
    int findLength(vector<int>& A, vector<int>& B) {
        int lenA = A.size();
        int lenB = B.size();
        if(lenA == 0 || lenB == 0)
            return 0;
        //申请dp二维数组
        vector<vector<int>> dp(lenA, vector<int> (lenB, 0));
        int maxLen = 0;
        int maxEnd = 0;
        //初始化边界条件
        for(int i = 0; i < lenA; i++){
            if(A[i] == B[0]){
                dp[i][0] = 1;
                maxLen = 1;
                maxEnd = i;
            }
        }
        for(int j = 0; j < lenB; j++){
            if(A[0] == B[j]){
                dp[0][j] = 1;
                maxLen = 1;
                maxEnd = 0;
            }
        }
        //递推
        for(int i = 1; i < lenA; i++){
            for(int j = 1; j < lenB; j++){
                if(A[i] == B[j]){
                    dp[i][j] = 1 + dp[i - 1][j - 1];
                    if(dp[i][j] > maxLen){
                        maxLen = dp[i][j];
                        maxEnd = i;
                    }
                }
                else{
                    dp[i][j] = 0;
                }
            }
        }
        return maxLen;
    }
};

leetcode这一题只需要返回最长的长度,而不需要返回重复的数组。如果要返回重复的数组也很简单,我在代码中记录maxLen的地方同时也记录了得到maxLen的maxEnd,这个变量是用来记录取到最长子数组的最后一个位置,由于存的是i,因此是指在A中最后的位置,如果是j则对应为B。因此最终返回的子数组就是A中i的位置往前长度为manLen的子数组。

3. 代码简化

以上的代码是按照dp的过程写的,实际上初始化和递推在这一题中是可以合并到一个循环里写的。
代码如下:

class Solution {
public:
    int findLength(vector<int>& A, vector<int>& B) {
        int lenA = A.size();
        int lenB = B.size();
        if(lenA == 0 || lenB == 0)
            return 0;
        //申请dp二维数组
        vector<vector<int>> dp(lenA, vector<int> (lenB, 0));
        int maxLen = 0;
        int maxEnd = 0;
        //初始化边界条件
        //递推
        for(int i = 0; i < lenA; i++){
            for(int j = 0; j < lenB; j++){
                if(A[i] == B[j]){
                    if(i == 0 || j == 0)
                        dp[i][j] = 1;
                    else{
                        dp[i][j] = 1 + dp[i - 1][j - 1];
                        if(dp[i][j] > maxLen){
                            maxLen = dp[i][j];
                            maxEnd = i;
                        }
                    }
                }
                else{
                    dp[i][j] = 0;
                }
            }
        }
        return maxLen;
    }
};