题目描述
给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
示例 1:
输入:
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
使用算法
使用动态规划(Dynamic Programming)算法:
- 递归到动态规划的一般转化方法
递归函数有n个参数,就定义一个n维数组,数组的下标是递归函数参数的取值范围,数组元素的值是递归函数的返回值,这样就可以从边界值开始,逐步填充数组,相当于计算递归函数值的逆过程。 - 能用动态规划解决的问题的特点:
1.问题具有最优子结构性质
2.无后效性 - 动态规划解题的一般步骤:
1.将原问题分解为子问题
2.确定状态
3.确定一些初始状态(边界状态)的值
4.确定状态转移方程
解题思路
- 分割子问题
设DP[i][j]矩阵表示以A[i-1]和B[j-1]为结尾元素的公共子数组的长度(注意:在最后形成的公共子数组结果中,最后一个元素必须为A[i-1]和B[j-1],而不是说以A[i-1]和B[j-1]结尾的子数组再去寻找公共子数组)。 - 确定边界条件
DP[0][j]和DP[i][0]分别表示A和B为空串,显然结果都为0,以此作为边界条件。 - 分情况讨论递推公式:
当新增加的元素使两个子数组结尾元素相同时,DP[i][j]=DP[i-1][j-1]。
当新增加的元素使两个子数组结尾元素不同时,因为不可能存在结尾不同的公共子数组,所以DP[i][j]=0。
对于题目所给示例而言,DP矩阵如下:
A\B | 空串 | 3 | 2 | 1 | 4 | 7 |
---|---|---|---|---|---|---|
空串 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 1 | 0 | 0 |
2 | 0 | 0 | 1 | 0 | 0 | 0 |
3 | 0 | 1 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 2 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 3 | 0 | 0 |
- 确定原问题与子问题关系
因为最后形成的最长公共子数组,一定是以A中某个元素结尾,B中某个原素结尾。所以最终所求问题,等价于寻找DP矩阵中最大元素,其对应的值即为最长公共子数组的长度。 - C++代码:
class Solution { public: int findLength(vector<int>& A, vector<int>& B) { int Max = 0; int lenA = A.size(); int lenB = B.size(); vector<vector<int>> DP(lenA + 1, vector<int>(lenB + 1, 0)); //设置边界条件 for(int i = 0; i <= lenA; i++) { DP[i][0] = 0; } for(int j = 0; j <= lenB; j++) { DP[0][j] = 0; } for(int i = 1; i <= lenA; i++) { for(int j = 1; j <= lenB; j++) { if(A[i - 1] == B[j - 1]) { DP[i][j] = DP[i- 1][j - 1] + 1; } else { DP[i][j] = 0; } Max = Max > DP[i][j] ? Max : DP[i][j]; } } return Max; } };