刷题记录
- *300. 最长递增子序列
- 674. 最长连续递增序列
- 基础解法(非动规)
- 动态规划
- 718. 最长重复子数组
- 滚动数组
*300. 最长递增子序列
leetcode题目地址
dp数组含义:
dp[i]表示以nums[i]结尾的最长递增子序列长度,即以nums[i]结尾的子序列的长度。
j从0向i遍历,遇到num[i] > num[j], dp[i] = max(dp[j]+1, dp[i]);
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
n
)
O(n)
O(n)
// java
class Solution {
public int lengthOfLIS(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
int result = 1;
// for(int i=0; i<len; i++) dp[i] = 1;
Arrays.fill(dp, 1);
for(int i=1; i<len; i++){
for(int j=0; j<i; j++){
if(nums[i] > nums[j]) dp[i] = Math.max(dp[i], dp[j]+1);
}
if(result < dp[i]) result = dp[i];
}
return result;
}
}
674. 最长连续递增序列
leetcode题目地址
基础解法(非动规)
求最长连续递增子序列,统计子序列记录最长即可。在递增中断时,计数器要置为1而非0,因为下一个子序列从当前元素开始。
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
// java
class Solution {
public int findLengthOfLCIS(int[] nums) {
int result = 1;
int cnt = 1;
int len = nums.length;
for(int i=1; i<len; i++){
if(nums[i]>nums[i-1]) {
cnt++;
if(cnt > result) result = cnt;
}
else cnt = 1; // 计数器置为1
}
return result;
}
}
动态规划
dp数组含义:
dp[i]表示以nums[i]结尾的最长连续递增子序列的长度。
初始化:
每个元素本身就是一个连续递增子序列,因此初始化为1,即dp数组均初始化为1。
// java
class Solution {
public int findLengthOfLCIS(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
Arrays.fill(dp, 1);
int result = 1;
for(int i=1; i<len; i++){
if(nums[i] > nums[i-1]) dp[i] = dp[i-1]+1;
result = Math.max(result, dp[i]);
}
return result;
}
}
718. 最长重复子数组
leetcode题目地址
dp数组含义:
dp[i][j]表示 以nums1[i-1]结尾的子数组A 和以 以nums2[j-1]结尾的子数组B 的最长重复子数组长度。
这里为什么要用i-1和j-1?
因为dp[i][j]的更新依赖于dp[i-1][j-1]的值。也就是说,在nums1[i-1]和nums2[j-1]相等时,更新对应位置长度需要依赖nums1[i-2]和nums2[j-2]的最长重复子数组长度。
以题目示例1举例:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
- 当nums1[2] == nums2[0]时,当前位置的最长重复子数组长度依赖于前面的匹配情况,前面相等的串长度为0,因此这里dp[3][1]是1。
- 当nums1[3] == nums2[1]时,逻辑同上,dp[4][2]的更新依赖于前面的匹配情况,前面有一个元素匹配到,因此这里dp[4][2] = dp[3][1]+1 = 2
- 当nums1[4] == nums2[2]时,逻辑同上,dp[5][3]的更新依赖于前面的匹配情况,前面有两个元素匹配到,因此这里dp[5][3] = dp[4][2]+1 = 3
到这里就可以总结出状态转移方程,dp[i][j] = dp[i-1][j-1] + 1
由于这里使用了i-1和j-1,在i和j为0时会越界。 因此整体将dp数组下标后移一位,来解决这一问题。(也可单独处理i和j为0的情况,较复杂)
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
n
)
O(n)
O(n)
// java
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int[][] dp = new int[len1+1][len2+1];
int result = 0;
if(nums1[0] == nums2[0]) dp[1][1] = 1;
for (int i=1; i<=len1; i++){
for(int j=1; j<=len2; j++){
if(nums1[i-1] == nums2[j-1]){
dp[i][j] = dp[i-1][j-1]+1;
}
result = Math.max(result, dp[i][j]);
// System.out.print(dp[i][j] + " ");
}
// System.out.println();
}
return result;
}
}
滚动数组
注意:
1、思路同上,只是每一层的状态是从上一层拷贝下来的,因此在遍历nums2时要从后向前,防止将前面元素在上一层的状态覆盖
2、当遇到元素不相同是要将对应位置赋值0.
// java
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int[] dp = new int[len2+1];
int result = 0;
for (int i=1; i<=len1; i++){
for(int j=len2; j>=1; j--){
if(nums1[i-1] == nums2[j-1]){
dp[j] = dp[j-1]+1;
} else dp[j] = 0; // 注意这里不相等的时候要有赋0的操作
result = Math.max(result, dp[j]);
}
}
return result;
}
}