目录
今天下午休息,合租的小伙伴陪女票出去玩了,在家闲来无事就随便刷刷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图便于理解
思路
两个字符串分别为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证明,没有证明也要提供没有证明的证明,英国人办事真的死板,呵呵哒。。