最长重复子数组:算法视频讲解与刷题指南

2024-10-27 10:04:32发布    浏览158次    信息编号:95606

友情提醒:凡是以各种理由向你收取费用,均有骗子嫌疑,请提高警惕,不要轻易支付。

最长重复子数组:算法视频讲解与刷题指南

我把所有的解题指南整理成:,方便大家在电脑上阅读。这个仓库每天都会更新。请大家去给个star支持一下吧!

同时,我在B站更新了算法视频,同名:代码随机记录

718. 最长重复子数组

问题链接:

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

例子:

进入:

答:[1,2,3,2,1]

B:[3,2,1,4,7]

输出:3

解释:

最长的公共子数组是 [3, 2, 1]。

暗示:

想法

注意,问题中提到的子数组实际上是一个连续的子序列。动态规则最适合解决此类问题。动态规则的五步分析如下:

判断dp数组(dp表)和下标的含义

dp[i][j]:A以下标i - 1结尾,B以下标j - 1结尾。最长重复子数组的长度为dp[i][j]。

这时候细心的同学应该发现了,dp[0][0]是什么意思呢?它不能是以下标-1结尾的A数组。

事实上,dp[i][j]的定义也决定了,当我们遍历dp[i][j]时,i和j都会从1开始。

然后有同学问了,于是我定义dp[i][j]为下标i结尾的A,下标j结尾的B,以及最长重复子数组的长度。不?

没关系!但实施起来比较麻烦一些。看下面的dp数组状态图就可以理解了。

确定递推公式

根据dp[i][j]的定义,dp[i][j]的状态只能从dp[i - 1][j - 1]推导出来。

即当A[i - 1]和B[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + 1;

根据递归公式我们可以看出,遍历i和j都是从1开始的!

如何初始化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。

例如A[0]与B[0]相同,则dp[1][1] = dp[0][0] + 1,只有dp[0][0]初始为0,这是一致的用递归公式一步步相加。

确定遍历顺序

外层for循环遍历A,内层for循环遍历B。

然后另一个同学问,外层for循环遍历B,内层for循环遍历A。不是吗?

好吧,同样的,这里我用外层for循环来遍历A,用内层for循环来遍历B。

同时,这题要求最长子数组的长度。所以遍历的时候,记录dp[i][j]的最大值。

代码如下:

for (int i = 1; i <= A.size(); i++) {
    for (int j = 1; j <= B.size(); 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];
    }
}

导出 dp 数组的示例

以例1,A:[1,2,3,2,1],B:[3,2,1,4,7]为例,画出一个dp数组的状态变化,如下:

718. 最长重复子数组

上述五个步骤分析完成后,C++代码如下:

class Solution {
public:
    int findLength(vector& A, vector& B) {
        vector> dp (A.size() + 1, vector(B.size() + 1, 0));
        int result = 0;
        for (int i = 1; i <= A.size(); i++) {
            for (int j = 1; j <= B.size(); 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;
    }
};

滚动数组

下图中:

718. 最长重复子数组

我们可以看到 dp[i][j] 是从 dp[i - 1][j - 1] 导出的。然后压缩成一维数组,即dp[j]由dp[j - 1]推导出来。

相当于将上层dp[i - 1][j]复制到下一层dp[i][j]继续使用。

这时候遍历B数组的时候就需要从后往前遍历,避免重复覆盖。

class Solution {
public:
    int findLength(vector& A, vector& B) {
        vector dp(vector(B.size() + 1, 0));
        int result = 0;
        for (int i = 1; i <= A.size(); i++) {
            for (int j = B.size(); j > 0; j--) {
                if (A[i - 1] == B[j - 1]) {
                    dp[j] = dp[j - 1] + 1;
                } else dp[j] = 0; // 注意这里不相等的时候要有赋0的操作 
                if (dp[j] > result) result = dp[j];
            }
        }
        return result;
    }
};

原版算法学习手册开放下载!

我把《代码说明》中的二叉树、回溯算法、贪心算法、背包问题等各种主题整理成PDF。它们是绝对必要且透明的!

我们先来预览一些截图:

没什么技巧,直接给出下载地址:

密码:vjqw

提醒:请联系我时一定说明是从奢侈品修复培训上看到的!