两种方法(滑动窗口, DP)解LeetCode水题:最长重复子数组

目录

题目

解题思路

滑动窗口

​思路

代码

结果

DP

思路

代码

结果


今天下午休息,合租的小伙伴陪女票出去玩了,在家闲来无事就随便刷刷LeetCode每日一题吧,结果推送给我的是一道难度中等的题,这是在侮辱我的智商吗?可后面的解题过程告诉我,我智商不用它来侮辱也是负数。话不多说,上题目。

题目

给两个整数数组 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

解题思路

一看到这题,我生锈的脑子第一反应就是暴力暴力暴力,暴力的时间复杂度为O(n3),最后的提交结果也毫无意外地超时了。

言归正传,再仔细阅读了一下题目,“公共的”、“长度最长的”这几个字眼再结合本题,不就在暗示我们用滑动窗口嘛。

滑动窗口

先上1张GIF图便于理解

fig1思路

两个字符串分别为A串和B串

1. 以A串为基准,B串向后滑动,每滑动一次比较两者公共部分(窗口)的元素,若连续相同则连续进行ret+1,若不同则ret不变。

2. 以B串为基准,A串向后滑动,每滑动一次比较两者公共部分(窗口)的元素,若连续相同则连续进行ret+1,若不同则ret不变。

3. 最后取两个ret最大值。

代码

public int findLength(int[] A, int[] B) {  //滑动窗口
        int res_num1=0,res_num2=0; //两次比较的两个结果,结果1和结果2
        for(int i=0;i<A.length;i++)  //以A串为基准,滑动B串
        {
            int i_point=i,temp=0;
            for(int j=0;j<A.length-i;j++) //窗口大小为0~A.length-1
            {
                if(B[j]==A[i_point])  //若窗口内元素相同,则temp+1,连续相同则temp连续+1
                {
                    temp += 1;
                    res_num1 = Math.max(res_num1,temp); //不断和结果1比较大小
                }
                else
                {
                    temp=0;  //若中间被打断,即窗口内A串和B串两个元素不同,那么temp归零。
                }
                i_point++; //A串工作指针指向窗口内A串的下个元素
            }
        }
        for(int i=0;i<B.length;i++)  //以B串为基准,滑动A串
        {
            int i_point=i,temp=0;
            for(int j=0;j<B.length-i;j++)
            {
                if(A[j]==B[i_point])
                {
                    temp+=1;
                    res_num2 = Math.max(res_num2,temp);
                }
                else
                {
                    temp=0;
                }
                i_point++;
            }
        }
        return Math.max(res_num1,res_num2);  //返回最大值
    }

结果

DP

做完之后看了下题解,发现也可以用动态规划,而且这题用动态规划做还挺简单的,状态转移方程简单得一腿。

思路

令 dp[i][j] 表示 A[i:] 和 B[j:] 的最长公共前缀,那么答案即为所有 dp[i][j] 中的最大值。如果 A[i] == B[j],那么 dp[i][j] = dp[i + 1][j + 1] + 1,否则 dp[i][j] = 0。(这里借用了 Python 表示数组的方法,A[i:] 表示数组 A 中索引 i 到数组末尾的范围对应的子数组。)

考虑到这里 dp[i][j] 的值从 dp[i + 1][j + 1] 转移得到,所以我们需要倒过来,首先计算 dp[len(A) - 1][len(B) - 1],最后计算 dp[0][0]。

以题目中的输入为例

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

状态转移方程为:

若 A[i] != B[j] , dp[i][j] = 0
若 A[i] == B[j] , dp[i][j] = dp[i+1][j+1] + 1

动态规划矩阵如下表所示:

  3 2 1 4 7
1 0 0 1 0 0
2 0 1 0 0 0
3 3 0 0 0 0
2 0 2 0 0 0
1 0 0 1 0 0

结果即为矩阵中的最大值3

代码

public int findLength(int[] A, int[] B) {  //动态规划
        int n = A.length, m = B.length;
        int[][] dp = new int[n + 1][m + 1];
        int ans = 0;
        for (int i = n - 1; i >= 0; i--) {
            for (int j = m - 1; j >= 0; j--) {
                dp[i][j] = A[i] == B[j] ? dp[i + 1][j + 1] + 1 : 0;
                ans = Math.max(ans, dp[i][j]);
            }
        }
        return ans;
    }

结果

————————————————————————————————————————

烦躁啊,被疫情搞得左右为难,不知道怎么选择,到底出国还是国内还是HK还是工作,办CAS又被学校卡了,说要提供之前KCL Summer Course的CEFR/RQF Level证明,没有证明也要提供没有证明的证明,英国人办事真的死板,呵呵哒。。

猜你喜欢

转载自blog.csdn.net/qq_38890412/article/details/107107488