
思路:题目中的子数组就是指连续子序列。
(1)暴力:要求两个数组中的最长重复子数组,就是两层for循环确定两个数组的起始位置,然后再来一个for循环可以是for或while,从两个起始位置开始比较,取得重复子数组的长度。
(2)动规:用二维数组记录两个字符串的所有比较情况,便于推导递推公式。
动规五部曲如下所示:
1.确定dp数组(dp table)及其下标的含义:
(1)dpij表示以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组的长度为dpij(以下标i - 1为结尾的A,一定是以Ai - 1为结尾的字符串)。
(2)dp00没有含义,本题遍历dpij时i和j都要从1开始。
2.确定递推公式:
(1)根据dpij的定义,dpij的状态只能由dpi - 1j - 1推导出来,即当Ai - 1和Bj - 1相等的时候,dpij = dpi - 1j - 1 + 1。
(2)根据递推公式可以看出,遍历i和j都要从1开始。
3.dp数组如何初始化:
(1)根据dpij的定义,dpi0和dp0j都是没有意义的,但dpi0和dp0j要赋初始值,为了方便递推公式dpij = dpi - 1j - 1 + 1。所以dpi0和dp0j初始化为0。
(2)举个例子,如果A0 = B0,那么dp11 = dp00 + 1,只有在dp00初始化为0时,正好符合递推公式逐步累加起来。
4.确定遍历顺序:外层for循环遍历A,内层for循环遍历B(反过来也行)。同时题目要求求长度最长的子数组的长度,所以在遍历时顺便把dpij的最大值记录下来。代码如下所示。
cpp
for (int i = 1; i <= nums1.size(); i++) {
for (int j = 1; j <= nums2.size(); j++) {
if (nums1[i - 1] == nums2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
if (dp[i][j] > result) result = dp[i][j];
}
}
5.举例推导dp数组:以示例1中,A1,2,3,2,1,B3,2,1,4,7为例,画一个dp数组的状态变化如下图所示。

附代码:
(一)dpij表示以下标i - 1结尾的A,和以下标j - 1结尾的B,最长重复子数组的长度为dpij。
java
//dp[i][j]表示以下标i - 1结尾的A,和以下标j - 1为结尾的B,最长重复子数组的长度为dp[i][j]
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int res = 0;
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
for(int i = 1;i < nums1.length + 1;i++){
for(int j = 1;j < nums2.length + 1;j++){
if(nums1[i - 1] == nums2[j - 1]){
dp[i][j] = dp[i - 1][j - 1] + 1;
res = Math.max(res,dp[i][j]);
}
}
}
return res;
}
}
(二)dpij表示以下标i结尾的A,和以下标j结尾的B,最长重复子数组的长度为dpij。
java
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int res = 0;
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
for(int i = 0;i < nums1.length;i++){
if(nums1[i] == nums2[0]){
dp[i][0] = 1;
}
}
for(int j = 0;j < nums2.length;j++){
if(nums2[j] == nums1[0]){
dp[0][j] = 1;
}
}
for(int i = 0;i < nums1.length;i++){
for(int j = 0;j < nums2.length;j++){
if(nums1[i] == nums2[j] && i > 0 && j > 0){//防止i - 1出现负数
dp[i][j] = dp[i - 1][j - 1] + 1;
}
if(dp[i][j] > res){
res = dp[i][j];
}
}
}
return res;
}
}
(三)滚动数组,一维dp。
思路:由于dpij都是由dpi - 1j - 1推出的,那么压缩为一维数组,dpj都是由dpj - 1推出的。也就是相当于把上一层的dpi - 1j拷贝到下一层的dpij来继续用。此时遍历数组B的时候,就是从后向前遍历,以避免重复覆盖。
附代码:
java
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int[] dp = new int[nums2.length + 1];
int res = 0;
for(int i = 1;i <= nums1.length;i++){
for(int j = nums2.length;j > 0;j--){
if(nums1[i - 1] == nums2[j - 1]){
dp[j] = dp[j - 1] + 1;
}else{
dp[j] = 0;
}
res = Math.max(res,dp[j]);
}
}
return res;
}
}