dynamic programming
- Mind Map Summary
- Introductory questions
- simple question
- Summary of the knapsack problem
- 01 Backpack*
- full backpack
- Multiple Backpacks
- Robbery Series
- stock series
-
- Best Time to Buy and Sell Stocks - Buy and Sell Once
- Best Time to Buy and Sell Stocks II - Buy and Sell Multiple Times
- Best Time to Buy or Sell Stocks III - Buy or Sell Max Twice
- The best time to buy and sell stocks IV - buy and sell at most k times
- The best time to buy and sell stocks includes a freezing period - buy and sell multiple times, sell for one day with a freezing period
- The best time to buy and sell stocks includes handling fees - buy and sell multiple times, each time there is a handling fee
- subsequence, subarray
- edit distance
- palindrome skewer
Mind Map Summary
Since CSDN has a picture size limit, only compressed pictures can be uploaded. . .
Classification of dynamic topics:
- knapsack problem A
- Robbery
- stock question
- subsequence problem
- …
Introductory questions
Fibonacci numbers
Topic: 509. Fibonacci Numbers
Standard DP:
public int fib(int n) {
if (n == 0) return 0;
// dp[i] 第 i 个斐波那契数
int[] dp = new int[n + 1];
// 初始化 dp 数组
dp[0] = 0;
dp[1] = 1;
// 递推
for (int i = 2; i <= n; i++)
dp[i] = dp[i - 1] + dp[i - 2];
return dp[n];
}
Rolling Array Ideas: Optimizing with Two Variables
public int fib(int n) {
if (n == 0) return 0;
int a = 0, b = 1;
for (int i = 2; i <= n; i++) {
b = a + b;
a = b - a;
}
return b;
}
climbing stairs
Topic: 70. Climbing stairs
public int climbStairs(int n) {
// dp[i] 爬到第 i 阶时的方法数量
int[] dp = new int[n + 1];
dp[0] = dp[1] = 1;
for (int i = 2; i <= n; i++)
dp[i] = dp[i - 1] + dp[i - 2];
return dp[n];
}
Backpack problem:
public int climbStairs(int n) {
// dp[i] 上第 i 层的方法数量
int[] dp = new int[n + 1];
dp[0] = 1;
for (int i = 1; i <= n; i++) // 背包
for (int j = 1; j <= 2; j++) // 物品
if (i >= j) dp[i] += dp[i - j];
return dp[n];
}
simple question
Climb stairs with minimum cost
You are given an array of integers cost
, where cost[i]
is i
the cost to climb up the -th step of the stairs. Once you pay this fee, you can choose to climb a flight of stairs or two. You can choose to start climbing the stairs
from the steps marked with 0
or with . Please calculate and return the minimum cost to reach the top of the stairs.1
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;
// dp[i] 爬到第 i 层的最低花费
int[] dp = new int[n + 1];
dp[0] = dp[1] = 0;
for (int i = 2; i <= n; i++)
dp[i] = Math.min(dp[i - 2] + cost[i - 2], dp[i - 1] + cost[i - 1]);
return dp[n];
}
different paths
Title: 62. Different paths
A robot is located in the upper left corner of a m x n
grid (the starting point is labeled "Start" in the figure below).
The robot can only move one step down or to the right at a time. The robot tries to reach the bottom right corner of the grid (marked "Finish" in the image below).
How many different paths are there in total?
输入:m = 3, n = 2
输出:3
解释:
---------
| s | o |
---------
| o | o |
---------
| o | e |
---------
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
public int uniquePaths(int m, int n) {
// dp[i][j] 到达 a[i][j] 的不同路径数
int[][] dp = new int[m][n];
// 原点开始一直 往下 或 往右 走, 路径都是 1
for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int i = 0; i < n; i++) dp[0][i] = 1;
// 递推
for (int i = 1; i < m; i++)
for (int j = 1; j < n; j++)
// 当前路径总数 = 从左过来的路径 + 从上过来的路径
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
return dp[m - 1][n - 1];
}
different paths II
Title: 63. Different Paths II
A robot is located in the upper left corner of a m x n
grid (the starting point is labeled "Start" in the figure below).
The robot can only move one step down or to the right at a time. The robot tries to reach the bottom right corner of the grid (marked "Finish" in the image below).
Now consider that there are obstacles in the grid. So how many different paths will there be from top left to bottom right?
Obstacles and empty positions in the 1
grid 0
are denoted by and , respectively.
输入:obstacleGrid =
[[0,0,0],
[0,1,0],
[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
public int uniquePathsWithObstacles(int[][] g) {
int m = g.length, n = g[0].length;
// dp[i][j] 到达 [i][j] 的不同路径数
int[][] dp = new int[m][n];
dp[0][0] = 0;
// 初始化: 从起点 往右 或 往下, 能走到的初始化为 1
for (int i = 0; i < m; i++) {
if (g[i][0] == 1) break;
dp[i][0] = 1;
}
for (int i = 0; i < n; i++) {
if (g[0][i] == 1) break;
dp[0][i] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (g[i][j] == 1) continue; // 遇到障碍, 跳过
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
integer split
Title: 343. Integer Splitting
Given a positive integer n
, split it into k
a sum ( ) of positive integersk >= 2
and maximize the product of these integers.
Returns the largest product you can get .
输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
public int integerBreak(int n) {
// dp[i] 整数 i 可以被拆分出的最大乘积
int[] dp = new int[n + 1];
dp[1] = dp[2] = 1;
for (int i = 3; i <= n; i++) {
for (int j = 1; j < i; j++) {
// 前 j 长度进行拆分的最大乘积: dp[j] * (i - j)
// 前 j 长度不进行拆分的最大乘积: j * (i - j)
dp[i] = Math.max(dp[i], Math.max(dp[j] * (i - j), j * (i - j)));
}
}
return dp[n];
}
Different Binary Search Trees*
Given an integer , how many binary search treesn
are there that consist of n
exactly nodes and whose node values vary1
from to ? Returns the number of binary search trees that satisfy the meaning of the question.n
输入:n = 3
输出:5
DP:
public int numTrees(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
// 作为根的元素 1, 2, ..., n
for (int i = 1; i <= n; i++)
// 根的左右子树的元素数量
// 根1 [0, n-1], 根2 [1, n-2], ... 根n [n-1, 0]
for (int j = 1; j <= i; j++)
dp[i] += dp[j - 1] * dp[i - j];
return dp[n];
}
recursion:
public int numTrees(int n) {
if (n <= 1) return 1;
int sum = 0;
for (int i = 1; i <= n; i++)
sum += numTrees(i - 1) * numTrees(n - i);
return sum;
}
Summary of the knapsack problem
Can the knapsack be filled (in fact, it is seeking the maximum value of the knapsack ): dp [ j ] = max ( dp [ j ] , dp [ j − nums [ i ] ] + nums [ i ] ) dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])dp[j]=max(dp[j],dp[j−nums[i]]+nums[i])
- separate equal sum subsets
- The Weight of the Last Stone II
Fill the knapsack with the maximum value : dp [ j ] = max ( dp [ j ] , dp [ j − w [ i ] ] + v [ i ] ) dp[j] = max(dp[j], dp[j - w [i]] + v[i])dp[j]=max(dp[j],dp[j−w[i]]+v [ i ])
- one and zero
There are several ways to fill the knapsack : dp [ j ] = dp [ j ] + dp [ j − nums [ i ] ] dp[j] = dp[j] + dp[j - nums[i]]dp[j]=dp[j]+dp[j−nums[i]]
- target and
- Change Change II
- Combined Sum IV
- Advanced stair climbing
The minimum number of items to fill the backpack : dp [ j ] = min ( dp [ j ] , dp [ j − coins [ i ] ] + 1 ) dp[j] = min(dp[j], dp[j - coins[i]] + 1)dp[j]=min(dp[j],dp[j−coins[i]]+1)
- change exchange
- perfect square number
The order of cycles in the complete knapsack problem:
- If you want to find the number of combinations, the outer for loop traverses the items, and the inner for traverses the backpack
- If you are looking for the number of permutations, the outer layer for traverses the backpack, and the inner layer for loops traverses the items
- Generally find the maximum value, the minimum value does not require the traversal order
[1,2]
and[2,1]
is permutation
[1,2]
is combination
01 Backpack*
have NNN items and a capacity isVVV 'sbackpack,each item can only be used once
iiThe volume of item i is vi v_ivi,This is wi w_iwi
Solve which items to put into the backpack, so that the total volume of these items does not exceed the backpack capacity, and the total value is the largest
class Solution {
// 二维数组实现
// v - 价值数组, w - 重量数组, c - 背包容量
static int maxValue(int[] v, int[] w, int c) {
// dp[i][j] 有前 i 个物品可选, 最大承重为 j 的背包的物品总价值
int[][] dp = new int[v.length + 1][c + 1];
for (int i = 1; i <= v.length; i++) // 物品
for (int j = 1; j <= c; j++) // 背包
// 容量不够放本次的物品
if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
// 容量够放本次物品: 比较 放 与 不放 获得的最大总价值来选择
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
return dp[v.length][c];
}
// 一维数组实现
static int maxValue1(int[] v, int[] w, int c) {
int[] dp = new int[c + 1];
for (int i = 1; i <= v.length; i++) // 物品
for (int j = c; j >= w[i - 1]; j--) // 背包
dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
return dp[c];
}
public static void main(String[] args) {
int[] values = {
6, 3, 5, 4, 6 }; // 价值
int[] weights = {
2, 2, 6, 5, 4 }; // 重量
int capacity = 10; // 背包容量
System.out.println(maxValue(values, weights, capacity));
System.out.println(maxValue1(values, weights, capacity));
}
}
separate equal sum subsets
You are given a non- empty array containing only positive integers . Please judge whether this array can be divided into two subsets so that the sum of the elements of the two subsets is equal.nums
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
Topic analysis: The sum of array elements is sum
, if some elements can be found in the array and are sum / 2
, then the array can be separated into two subsets
Convert to 01 backpack: the array of items is nums
(both value and weight nums[i]
), the capacity of the backpack is sum / 2
, find the maximum value that the backpack can hold
public boolean canPartition(int[] nums) {
int sum = Arrays.stream(nums).sum();
// 奇数, 必定无法分隔
if ((sum & 1) == 1) return false;
// 目标子集的元素和
int target = sum / 2;
// 转化成 01背包问题
// dp[i][j]代表可装物品为0-i,背包容量为j的情况下,背包内容量的最大价值
return maxValue(nums, nums, target) == target;
}
2D 01 Backpack:
int maxValue(int[] v, int[] w, int c) {
// dp[i][j] 在前 i 个物品中选择, 容量为 j 的最大物品价值
int[][] dp = new int[v.length + 1][c + 1];
for (int i = 1; i <= v.length; i++) {
for (int j = 1; j <= c; j++)
if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
}
return dp[v.length][c];
}
One-dimensional 01 backpack:
int maxValue(int[] v, int[] w, int c) {
int[] dp = new int[c + 1];
for (int i = 1; i <= v.length; i++)
for (int j = c; j >= w[i - 1] ; j--)
dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
return dp[c];
}
The Weight of the Last Stone II
There is a pile of stones stones
represented . where stones[i]
is i
the weight of the first stone.
Each turn, choose any two stones from among them , and smash them together. Suppose the weights of the stones are x
and y
, respectively x <= y
. Then the possible outcomes of crushing are as follows:
- If
x == y
, then both stones are completely crushed; - If
x != y
, then a stonex
of will be completely crushed, andy
a stone of weight will have a new weight ofy-x
.
In the end, at most one stone will remain . Returns the smallest . If there are no stones left, return0
.
输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
Topic analysis:
x == y
The corresponding stone is completely crushed (the difference is 0), andx != y
the corresponding big stone crushes the small stone and the remaining weight of the big stone is yx (the difference is y - x)- It can be found that the crushing process is actually the process of weight subtraction
- For two piles of stones, let them crush the final result is: the difference between the sum of the two piles of stones
- If you want the stone to be finally crushed to get the minimum weight, try to find two piles of stones with similar weights
public int lastStoneWeightII(int[] stones) {
// 核心思路:任意选 i 块石头,使它们的重量趋近于总重量的一半,因为这样和另一半抵消的差值就是最小的
// 背包容量为和的一半, 试图寻找尽量装满一半的背包的元素的和
// 最极端就是正好找到一半, 剩下的元素构成另一半, 得出结果为 0
int sum = Arrays.stream(stones).sum();
int max = maxValue(stones, stones, sum / 2);
return Math.abs(max - (sum - max));
}
2D 01 Backpack:
int maxValue(int[] w, int[] v, int c) {
// dp[i][j] 在前 i 个物品中选择, 容量为 j 的最大物品价值
int[][] dp = new int[w.length + 1][c + 1];
for (int i = 1; i <= w.length; i++) {
for (int j = 1; j <= c; j++) {
if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
}
}
return dp[w.length][c];
}
One-dimensional 01 backpack:
int maxValue(int[] v, int[] w, int c) {
int[] dp = new int[c + 1];
for (int i = 1; i <= v.length; i++)
for (int j = c; j >= w[i - 1] ; j--)
dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
return dp[c];
}
target and*
Subject: Goal and
You are given an array of integers nums
and an integer target
.
Adding '+'
or '-'
, and then concatenating all the integers creates an expression :
- For example,
nums = [2, 1]
, can be added2
before'+'
, added1
before'-'
, and then concatenated to get the expression"+2-1"
. Returns the number of distinct expressions
that can be constructed by the methods above that evaluate to equaltarget
to .
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
backtrace:
class Solution {
public int findTargetSumWays(int[] nums, int target) {
return dfs(nums, target, 0);
}
int dfs(int[] nums, int target, int idx) {
if (idx == nums.length) {
if (target == 0) return 1;
return 0;
}
return dfs(nums, target - nums[idx], idx + 1) + dfs(nums, target + nums[idx], idx + 1);
}
}
Memory search:
class Solution {
Map<String, Integer> map = new HashMap<>();
public int findTargetSumWays(int[] nums, int target) {
return dfs(nums, target, 0, 0);
}
int dfs(int[] nums, int target, int sum, int idx) {
if (nums.length == idx) {
if (target == sum) return 1;
else return 0;
}
String key = sum + "&" + idx;
if (map.containsKey(key)) return map.get(key);
int val = dfs(nums, target, sum + nums[idx], idx + 1) + dfs(nums, target, sum - nums[idx], idx + 1);
map.put(key, val);
return val;
}
}
Two-dimensional DP:
// sumA - 正数和, sumB - 负数和, sum - nums 的和
// sumA + sumB = target
// sumA - sumB = sum
// sumA = (target + sum) / 2
// 同时可以发现 target + sum 必须是偶数, 否则无解
// 转化成 01背包, dp[i][j] 从前 i 个数字中选出若干个, 使得被选出的数字其和为 j 的方案数目
public int findTargetSumWays(int[] nums, int target) {
int sum = Arrays.stream(nums).sum();
if ((target + sum) % 2 == 1) return 0; // 奇数, 无解
// 若 target > sum, 无法凑够; target 太小导致 target < -sum,也凑不够
if (sum < Math.abs(target)) return 0;
// dp[i][j] 从前 i 个数字中选出若干个, 使得被选出的数字其和为 j 的方案数目
// dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]]
int c = (sum + target) >> 1;
int[][] dp = new int[nums.length + 1][c + 1];
dp[0][0] = 1; // dp[i][0] 除了 dp[0][0] = 1, 有多个数字的情况下, 和为 0 的情况可能有多个
for (int i = 1; i <= nums.length; i++) {
for (int j = 0; j <= c; j++) {
// j 从 0开始, 因为数字和可以为 0 (这点和背包有区别)
// 容量有限, 无法更新
if (j < nums[i - 1]) dp[i][j] = dp[i - 1][j];
// 可以选择第 i 个数字 nums[i - 1], 也可以不选, s两者和
else dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
}
}
return dp[nums.length][c];
}
One-dimensional:
public int findTargetSumWays(int[] nums, int target) {
int sum = Arrays.stream(nums).sum();
if ((target + sum) % 2 == 1) return 0;
if (sum < Math.abs(target)) return 0;
// dp[j] 选出数字和为 j 的方案数目 dp[j] += dp[j - nums[i]]
int c = (sum + target) >> 1;
int[] dp = new int[c + 1];
dp[0] = 1;
for (int i = 1; i <= nums.length; i++) {
for (int j = c; j >= 0; j--) {
// j 从 0开始, 因为数字和可以为 0 (这点和背包有区别)
// 容量有限, 无法更新
if (j < nums[i - 1]) continue;
// 可以选择第 i 个数字 nums[i - 1], 也可以不选, s两者和
dp[j] += dp[j - nums[i - 1]];
}
}
return dp[c];
}
one and zero
Given an array of binary strings strs
and two integers m
and n
.
Please find and strs
return the length of the largest subset of which has at most and and . The set is if all elements of are also elements of .m
0
n
1
x
y
x
y
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
This question seeks the length of the largest subset, which can be understood as the value of the item put in each time the condition is met is 1 (that is, the quantity + 1)
dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j-n_0][k-n_1] + 1)
- n_0 为当前字符串中 0 的个数
- n_1 为当前字符串中 1 的个数
3D DP:
public int findMaxForm(String[] strs, int m, int n) {
int len = strs.length;
// dp[i][j][k] 在字符数组的前 i 个字符串中, 使用 j 个 0, k 个 1, 的最大字符串的个数
int[][][] dp = new int[len + 1][m + 1][n + 1];
for (int i = 1; i <= len; i++) {
// 物品
for (int j = 0; j <= m; j++) {
// 背包1
for (int k = 0; k <= n; k++) {
// 背包2
int n_1 = getOneNum(strs[i - 1]); // 1 的数量
int n_0 = strs[i - 1].length() - n_1; // 0 的数量
// 不放物品
if (n_0 > j || n_1 > k) dp[i][j][k] = dp[i - 1][j][k];
// 放物品
else dp[i][j][k] = Math.max(dp[i - 1][j][k],
dp[i - 1][j - n_0][k - n_1] + 1); // 此处 +1 其实是价值
}
}
}
return dp[len][m][n];
}
// 获取字符中 '1' 的个数
int getOneNum(String s) {
int cnt = 0;
for (char c : s.toCharArray())
if (c == '1') cnt++;
return cnt;
}
Two-dimensional DP: (difficult to write directly, from three-dimensional optimization)
public int findMaxForm(String[] strs, int m, int n) {
int len = strs.length;
// dp[i][j] 在字符数组的字符串中,使用 i 个 0, j 个 1, 的最大字符串的个数
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= len; i++) {
int n_1 = getOneNum(strs[i - 1]); // 1 的数量
int n_0 = strs[i - 1].length() - n_1; // 0 的数量
for (int j = m; j >= n_0; j--)
for (int k = n; k >= n_1; k--)
dp[j][k] = Math.max(dp[j][k], dp[j - n_0][k - n_1] + 1);
}
return dp[m][n];
}
// 获取字符中 '1' 的个数
int getOneNum(String s) {
int cnt = 0;
for (char c : s.toCharArray())
if (c == '1') cnt++;
return cnt;
}
full backpack
There are N items and a knapsack of capacity V, each item has an infinite number of items available
iiThe volume of i items isvi v_ivi,This is wi w_iwi
Solve which items to put into the backpack, so that the total volume of these items does not exceed the backpack capacity, and the total value is the largest
public class Solution {
// 朴素版
// v - 价值数组, w - 重量数组, c - 背包容量
static int maxValue(int[] v, int[] w, int c) {
int[][] dp = new int[v.length + 1][c + 1];
for (int i = 1; i <= v.length; i++) // 物品
for (int j = 1; j <= c; j++) // 背包容量
for (int k = 0; k * w[i - 1] <= j; k++) // 物品数量
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - w[i - 1] * k] + v[i - 1] * k);
return dp[w.length][c];
}
// 二维数组实现
static int maxValue1(int[] v, int[] w, int c) {
// dp[i][j] 有前 i 个物品可选, 最大承重为 j 的背包的物品总价值
int[][] dp = new int[v.length + 1][c + 1];
for (int i = 1; i <= v.length; i++) // 物品
for (int j = 1; j <= c; j++) // 背包
// 容量不够放本次的物品
if (j < w[i - 1]) dp[i][j] = dp[i - 1][j];
// 容量够放本次物品
else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - w[i - 1]] + v[i - 1]);
return dp[v.length][c];
}
// 一维数组实现
static int maxValue2(int[] v, int[] w, int c) {
int[] dp = new int[c + 1];
for (int i = 1; i <= v.length; i++)
for (int j = w[i - 1]; j <= c; j++)
dp[j] = Math.max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
return dp[c];
}
public static void main(String[] args) {
int[] values = {
6, 3, 5, 4, 6 }; // 价值
int[] weights = {
2, 2, 6, 5, 4 }; // 重量
int capacity = 10; // 背包容量
System.out.println(maxValue(values, weights, capacity)); // 朴素版
System.out.println(maxValue1(values, weights, capacity)); // 二维数组
System.out.println(maxValue2(values, weights, capacity)); // 一维数组
}
}
Change Change II
You are given an array of integers coins
representing coins of different denominations and an array of integers amount
representing the total amount.
Please calculate and return the number of coin combinations that can make up the total amount. If no combination of coins can make up the total amount, return 0
.
Suppose there are an infinite number of coins of each denomination.
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
The order of cycles in the complete knapsack problem:
- If you want to find the number of combinations, the outer for loop traverses the items, and the inner for traverses the backpack
- If you are looking for the number of permutations, the outer layer for traverses the backpack, and the inner layer for loops traverses the items
This question is a combination problem , and the items should be traversed first and then the backpack.
[1, 2, 2]
and[2, 2, 1]
are the same, so are combinations (regardless of order)
0 1 2 3 4 5 (背包容量 j)
1 0 0 0 0 0 没有硬币的时候)
=======================
0 1 2 3 4 5 (背包容量 j)
1 1 1 1 1 1 1
=======================
0 1 2 3 4 5 (背包容量 j)
1 1 1 1 1 1 1
2 2 2 3 3
有了面值为 2 的硬币后:
- 如果不用,方案数还是 dp[j] 种
- 如果用了,看看在放入这枚硬币前,也就是背包容量为 [j-coins[i]] 的时候有几种方案
- 两种情况加起来,所以就是 dp[j] = dp[j] + dp[j-coins[i]]
========================
0 1 2 3 4 5 (背包容量 j)
1 1 1 1 1 1 1
2 2 2 3 3
5 4
One-dimensional array solution:
public int change(int amount, int[] coins) {
// dp[i] 凑出总金额为 i 的硬币组合数
// 选择硬币 coins[i], 当前硬币组合数量有 dp[j - coins[i]] 种
// 不选择硬币 coins[i], 当前硬币组合数量为 dp[j] 种
// 总的组合数量为: dp[j] = dp[j] + dp[j - coins[i]]
int[] dp = new int[amount + 1];
dp[0] = 1; // 金额为 0 时, 什么都不装, 只有这一种方式
for (int i = 0; i < coins.length; i++) // 物品
for (int j = coins[i]; j <= amount; j++) // 背包
dp[j] += dp[j - coins[i]];
return dp[amount];
}
Two-dimensional array solution:
public int change(int amount, int[] coins) {
// dp[i][j] 从前 i 个面额的硬币中选择, 凑出总金额为 j 的硬币的组合数
int[][] dp = new int[coins.length + 1][amount + 1];
// 初始化 dp 数组,金额为 0 时只有一种情况,也就是什么都不装
for (int i = 0; i <= coins.length; i++) dp[i][0] = 1;
for (int i = 1; i <= coins.length; i++) {
// 物品
for (int j = 1; j <= amount; j++) {
// 背包
if (j < coins[i - 1]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i - 1]];
}
}
return dp[coins.length][amount];
}
Another common way of writing:
for (int i = 1; i <= coins.length; i++) {
// 物品
for (int j = 1; j <= amount; j++) {
// 背包
dp[i][j] = dp[i - 1][j];
if (j >= coins[i - 1]) dp[i][j] += dp[i][j - coins[i - 1]];
}
}
Combined Sum IV
Topic: 377. Combined sums IV
You are given an array of distinct integers nums
, and a target integer target
. Please find out nums
from and return target
the number of element combinations whose sum is .
The question data guarantees that the answer fits within the range of 32-bit integers.
输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
This question is a permutation problem , and the backpack capacity should be traversed first, and then the items should be traversed.
It is different for
[1, 2]
and , so it is a permutation (related to the order)[2, 1]
One-dimensional DP:
public int combinationSum4(int[] nums, int target) {
// dp[i] 总和为 i 的元素排列个数
// 选这个数: dp[i - nums[j]]
// 不选这个数: dp[i]
int[] dp = new int[target + 1];
dp[0] = 1;
for (int i = 1; i <= target; i++) // 背包
for (int j = 0; j < nums.length; j++) // 物品
if (i >= nums[j]) // 背包放的下
dp[i] += dp[i - nums[j]];
return dp[target];
}
Two-dimensional DP: (a bit difficult to understand)
public int combinationSum4(int[] nums, int target) {
// dp[i][j] 在前 i 个元素中总和为 j 的排列个数
int[][] dp = new int[nums.length + 1][target + 1];
// 总和为 0, 一个数也不选, 视为一种情况
for (int i = 0; i <= nums.length; i++) dp[i][0] = 1;
for (int j = 1; j <= target; j++) // 背包
for (int i = 1; i <= nums.length; i++) // 物品
for (int k = i; k > 0; k--)
if (j - nums[k - 1] >= 0)
dp[i][j] += dp[i][j - nums[k - 1]];
return dp[nums.length][target];
}
Climbing Stairs (Advanced Version)
Original title: 70. Climbing Stairs - LeetCode
Advanced: step by step, two steps, three steps, ... until m steps. How many different ways are there to get to the top of the building?
Level 1, level 2, ... the m level is the item, and the roof is the backpack.
Each level can be used repeatedly, for example, after skipping 1 level, you can continue to jump 1 level.
Asking how many ways to jump to the top of the building is actually asking how many ways to fill your backpack.
public int climbStairs(int n) {
// dp[i] 爬到 i 楼的方法数
int[] dp = new int[n + 1];
dp[0] = 1;
for (int i = 1; i <= n; i++) // 背包
for (int j = i; j <= m; j++) // 物品
if (i >= j) dp[i] += dp[i - j]; // m 换成 2 可以通过 Leetcode 的爬楼梯
return dp[n];
}
change exchange
You are given an array of integers coins
representing coins of different denominations and an integer amount
representing the total amount.
Calculate and return the minimum . If no combination of coins can make up the total amount, return -1
.
You can consider the amount of each coin to be infinite.
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
This question has nothing to do with the traversal order, because it is to find the minimum value.
Generally, the minimum value is sought, and the DP array needs to be initialized to the MAX value.
public int coinChange(int[] coins, int amount) {
// dp[i] 凑到总金额为 i 需要的最少硬币个数
int[] dp = new int[amount + 1];
Arrays.fill(dp, 1, dp.length, Integer.MAX_VALUE); // dp[0] = 0
for (int i = 0; i < coins.length; i++) // 物品
for (int j = coins[i]; j <= amount; j++) // 背包
if (dp[j - coins[i]] != Integer.MAX_VALUE)
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}
perfect square number
Given an integer n
, return the least number of perfect squares that sum n
to .
A perfect square is an integer whose value is equal to the square of another integer; in other words, whose value is equal to the product of an integer multiplied by itself. For example, 1
, 4
, 9
and 16
are all perfect squares, but 3
and 11
are not .
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
A perfect square number is an item (can be used infinitely), and a positive integer n is a knapsack. How many items are there at least when this knapsack is filled?
Items first, then backpack:
public int numSquares(int n) {
// dp[i] 和为 i 的完全平方数的最少数量
int[] dp = new int[n + 1];
Arrays.fill(dp, 1, dp.length, Integer.MAX_VALUE); // dp[0] = 1
for (int i = 1; i * i <= n; i++) // 物品
for (int j = i * i; j <= n; j++) // 背包
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
return dp[n];
}
Backpack first, then items:
public int numSquares(int n) {
// dp[i] 和为 i 的完全平方数的最少数量
int[] dp = new int[n + 1];
Arrays.fill(dp, 1, dp.length, Integer.MAX_VALUE);
for (int i = 0; i <= n; i++) // 背包
for (int j = 1; j * j <= i; j++) // 物品
dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
return dp[n];
}
word splitting
Given a string s
and a list of strings wordDict
as dictionaries. Please judge whether you can use the words that appear in the dictionary to splice it out s
.
Note : It is not required to use all the words in the dictionary, and the words in the dictionary can be used repeatedly.
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
public boolean wordBreak(String s, List<String> wordDict) {
// dp[i] s[0..i] 能否拆分成 wordDict
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i <= s.length(); i++) // 背包
for (int j = 0; j < i; j++) // 物品
if (wordDict.contains(s.substring(j, i)) && dp[j])
dp[i] = true;
return dp[s.length()];
}
Multiple Backpacks
have NNN items and a capacity isVVV 's backpack.
SectionIIi items have at mostsi s_isipieces, the volume of each piece is vi v_ivi,This is wi w_iwi.
Solve which items to put into the backpack, so that the sum of the volume of the items does not exceed the capacity of the backpack, and the sum of the values is the largest.
There is no corresponding topic on LeetCode for multiple backpacks, let's find out.
class Solution {
static int maxValue(int[] nums, int[] w, int[] v, int c) {
int[] dp = new int[c + 1];
for (int i = 0; i < w.length; i++) // 物品
for (int j = c; j >= w[i]; j--) {
// 背包容量
for (int k = 1; k <= nums[i] && k * w[i] <= j; k++) // 遍历个数
dp[j] = Math.max(dp[j], dp[j - k * w[i]] + k * v[i]);
System.out.println(Arrays.toString(dp));
}
return dp[c];
}
public static void main(String[] args) {
int[] nums = new int[] {
2, 3, 2 };
int[] w = new int[] {
1, 3, 4 }; // 重量
int[] v = new int[] {
15, 20, 30 }; // 价值
int c = 10; // 背包容量
System.out.println(maxValue(nums, w, v, c));
}
}
Robbery Series
Robbery
Title: 198. Robbery - LeetCode
You are a professional thief planning to steal houses along the street. There is a certain amount of cash hidden in each room. The only restrictive factor that affects your theft is that the adjacent houses are equipped with interconnected anti-theft systems. If two adjacent houses are broken into by thieves on the same night, the system will automatically call the police. .
Given an array of non-negative integers representing the amount stored in each house, calculate the maximum amount you can steal in one night without triggering the alarm.
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
public int rob(int[] nums) {
int n = nums.length;
// dp[i] 偷第 i 号房屋时的最高金额
int[] dp = new int[n + 1];
dp[1] = nums[0];
for (int i = 2; i <= n; i++)
// 今天偷 或 不偷 两种选择
dp[i] = Math.max(dp[i - 2] + nums[i - 1], dp[i - 1]);
return dp[n];
}
Dakkakosha II
You are a professional thief planning to rob houses along the street, each of which has a certain amount of cash hidden in it. All the houses in this place are in a circle , which means that the first house and the last house are right next to each other. At the same time, adjacent houses are equipped with interconnected anti-theft systems. If two adjacent houses are broken into by thieves at the same night, the system will automatically call the police .
Given an array of non-negative integers representing the amount stored in each house, calculate the maximum amount you can steal tonight without setting off the alarm.
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
Since the head and the tail are the same, remove the head and the tail respectively [0, n - 1]
, [1, n]
steal in and , and return the maximum value of the two results
class Solution {
public int rob(int[] nums) {
if (nums.length == 1) return nums[0];
return Math.max(
help(Arrays.copyOfRange(nums, 0, nums.length - 1)),
help(Arrays.copyOfRange(nums, 1, nums.length)));
}
public int help(int[] nums) {
int n = nums.length;
int[] dp = new int[n + 1];
dp[1] = nums[0];
for (int i = 2; i <= n; i++)
dp[i] = Math.max(dp[i - 2] + nums[i - 1], dp[i - 1]);
return dp[n];
}
}
Robbery III TODO
stock series
Stock question:
- 121. The best time to buy and sell stocks ): buy and sell only once
- 122. The best time to buy and sell stocks II: You can buy and sell multiple times
- 123. The best time to buy and sell stocks III: buy and sell at most twice
- 188. The best time to buy and sell stocks IV: buy and sell at most k times
-
- The best time to buy and sell stocks includes a freezing period: buy and sell multiple times, sell for one day with a freezing period
- 714. The best time to buy and sell stocks includes handling fees: buy and sell multiple times, each time there is a handling fee
Best Time to Buy and Sell Stocks - Buy and Sell Once
Given an array prices
, its i
th element prices[i]
represents the price of a given stock on the th i
day .
You can only choose to buy the stock on one day and sell it on a different day in the future. Design an algorithm to calculate the maximum profit you can make.
Returns the maximum profit you can make from this trade. If you can't make any profit, return 0
.
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
DP:
public int maxProfit(int[] prices) {
// dp[i] 第 i 天卖出股票(当天就卖)能获取的最大利润
int[] dp = new int[prices.length];
// min 记录 i 之前天数的最大值
int min = prices[0], res = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < min) min = prices[i];
dp[i] = prices[i] - min; // 今天的利润
res = Math.max(res, dp[i]);
}
return res;
}
Greedy: Find from left to right, the difference between the minimum value on the left and the maximum value on the right
There is no need to calculate the minimum value on the left and the maximum value on the right and make the difference. You
only need to traverse from left to right, maintain the minimum value on the left, and then maintain the difference between the current traversed value and the minimum value.
public int maxProfit(int[] prices) {
int min = Integer.MAX_VALUE, res = 0;
for (int i = 0; i < prices.length; i++) {
min = Math.min(min, prices[i]);
res = Math.max(res, prices[i] - min);
}
return res;
}
Best Time to Buy and Sell Stocks II - Buy and Sell Multiple Times
You are given an array of integers prices
, where prices[i]
represents i
the price of a certain stock on day .
On each day, you can decide whether to buy and/or sell stocks. You can only hold at most one share of stock at any one time . You can also buy first and sell later on the same day. Returns the maximum .
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
总利润为 4 + 3 = 7 。
DP:
public int maxProfit(int[] prices) {
int n = prices.length;
// buy[i] 在第 i 天买入股票(不一定当天买)的最大利润
int[] buy = new int[n];
buy[0] = -prices[0];
// sell[i] 在第 i 天卖出股票(不一定当卖卖)的最大利润
int[] sell = new int[n];
for (int i = 1; i < n; i++) {
// 选择: [当前不买(之前买)] 或 [当前买]
buy[i] = Math.max(buy[i - 1], sell[i - 1] - prices[i]);
// 选择: [当前不卖(之前卖)] 或 [当前卖]
sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]);
}
return sell[n - 1];
}
greedy:
public int maxProfit(int[] prices) {
int res = 0;
for (int i = 1; i < prices.length; i++)
if (prices[i] > prices[i - 1])
res += prices[i] - prices[i - 1];
return res;
}
Best Time to Buy or Sell Stocks III - Buy or Sell Max Twice
Given an array whose i i
- element is i
the price of a given stock on day i.
Design an algorithm to calculate the maximum profit you can make. You can complete up to two transactions.
Note : You cannot participate in multiple transactions at the same time (you must sell the previous stock before buying again).
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
public int maxProfit(int[] prices) {
int n = prices.length;
// sell[i][j] 在第 i 天不持股(当天或之前卖出)的最大利润, 当前是第 j 笔交易
int[][] sell = new int[n][3];
// buy[i][j] 在第 i 天持股(当天或之前买入)的最大利润, 当前是第 j 笔交易
int[][] buy = new int[n][3];
for (int i = 0; i < 3; i++) buy[0][i] = -prices[0];
for (int i = 1; i < n; i++) {
for (int j = 1; j < 3; j++) {
// 卖的必须是当前轮买的
sell[i][j] = Math.max(sell[i - 1][j], buy[i - 1][j] + prices[i]);
// 买之前必须把上一轮卖掉
buy[i][j] = Math.max(buy[i - 1][j], sell[i- 1][j - 1] - prices[i]);
}
}
int max = 0;
for (int i = 0; i < 3; i++) max = Math.max(max, sell[n - 1][i]);
return max;
}
The best time to buy and sell stocks IV - buy and sell at most k times
Given an array of integers prices
, its i
th element prices[i]
is the price of a given stock on i
day .
Design an algorithm to calculate the maximum profit you can make. You can complete at most k transactions.
Note : You cannot participate in multiple transactions at the same time (you must sell the previous stock before buying again).
输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
public int maxProfit(int k, int[] prices) {
int n = prices.length;
// sell[i][j] 在第 i 天不持股(当天或之前卖出)的最大利润, 当前是第 j 笔交易
int[][] sell = new int[n][k + 1];
// buy[i][j] 在第 i 天持股(当天或之前买入)的最大利润, 当前是第 j 笔交易
int[][] buy = new int[n][k + 1];
for (int i = 0; i < k + 1; i++) buy[0][i] = -prices[0];
for (int i = 1; i < n; i++) {
for (int j = 1; j < k + 1; j++) {
// 卖的必须是当前轮买的
sell[i][j] = Math.max(sell[i - 1][j], buy[i - 1][j] + prices[i]);
// 买之前必须把上一轮卖掉
buy[i][j] = Math.max(buy[i - 1][j], sell[i - 1][j - 1] - prices[i]);
}
}
int max = 0;
for (int i = 0; i < k + 1; i++) max = Math.max(max, sell[n - 1][i]);
return max;
}
The best time to buy and sell stocks includes a freezing period - buy and sell multiple times, sell for one day with a freezing period
Topic: 309. The best time to buy and sell stocks includes a freezing period - LeetCode
Given an array of integers prices
where the th prices[i]
represents i
the stock price on the th day.
Design an algorithm to calculate the maximum profit. You can complete as many transactions as possible (buy and sell a stock multiple times) subject to the following constraints:
- After selling the stock, you cannot buy the stock the next day (that is, the freezing period is 1 day).
Note : You cannot participate in multiple transactions at the same time (you must sell the previous stock before buying again).
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
public int maxProfit(int[] prices) {
int n = prices.length;
// sell[i] 第 i 天卖出的最大收益
int[] sell = new int[n];
// buy[i] 第 i 天买入的最大收益
int[] buy = new int[n];
// cool[i] 第 i 天冷冻的最大收益
int[] cool = new int[n];
buy[0] = -prices[0];
for (int i = 1; i < n; i++) {
// 不买 或 买(只能在冷冻期过后买)
buy[i] = Math.max(buy[i - 1], cool[i - 1] - prices[i]);
// 不卖 或 卖
sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]);
// 冷冻期只会由昨天卖出得到
cool[i] = sell[i - 1];
}
return Math.max(sell[n - 1], cool[n - 1]);
}
The best time to buy and sell stocks includes handling fees - buy and sell multiple times, each time there is a handling fee
Topic: 714. The best time to buy and sell stocks includes handling fees - LeetCode
Given an array of integers prices
, where prices[i]
represents i
the stock price on the day; the integer fee
represents the handling fee for trading stocks.
You can complete transactions an unlimited number of times, but you need to pay a transaction fee for each transaction. If you have already bought a stock, you cannot continue to buy another stock until you sell it.
Returns the maximum value of profit.
Note : A transaction here refers to the entire process of buying, holding and selling stocks, and you only need to pay a handling fee for each transaction.
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
public int maxProfit(int[] prices, int fee) {
int n = prices.length;
// buy[i] 第 i 天时买入股票的最大收益
int[] buy = new int[n];
// sell[i] 第 i 天时卖出股票的最大收益
int[] sell = new int[n];
buy[0] = -prices[0];
for (int i = 1; i < n; i++) {
// 当前不买(之前买) 或 当前买
buy[i] = Math.max(buy[i - 1], sell[i - 1] - prices[i]);
// 当前不卖(之前卖) 或 当前卖
sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i] - fee);
}
return sell[n - 1];
}
subsequence, subarray
The subsequence is discontinuous, just keep the relative order of the elements
Subarrays and substrings are continuous, and there cannot be more or less elements between elements
longest increasing subsequence
Given an array of integers nums
, find the length of the longest strictly increasing subsequence in it .
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
public int lengthOfLIS(int[] nums) {
// dp[i] 以 i 位置结尾的最长递增子序列的长度
int[] dp = new int[nums.length];
Arrays.fill(dp, 1); // 每个位置的上升子序列至少是 1
int res = -1;
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < i; j++)
if (nums[j] < nums[i])
dp[i] = Math.max(dp[i], dp[j] + 1);
res = Math.max(res, dp[i]);
}
return res;
}
longest continuous increasing sequence
Title: 674. Longest Continuous Increasing Sequence - LeetCode
Given an unsorted array of integers, find the longest continuously increasing subsequence and return the length of that sequence.
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
DP:
public int findLengthOfLCIS(int[] nums) {
// dp[i] 以 i 位置结尾的最长连续递增序列的长度
int[] dp = new int[nums.length];
Arrays.fill(dp, 1); // 长度至少为 1
int res = 1;
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] < nums[i + 1])
dp[i + 1] = dp[i] + 1;
res = Math.max(res, dp[i + 1]);
}
return res;
greedy:
public int findLengthOfLCIS(int[] nums) {
int cnt = 1, res = 1;
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] < nums[i + 1]) cnt++;
else cnt = 1;
res = Math.max(res, cnt);
}
return res;
}
longest repeating subarray
Given two integer arrays nums1
and nums2
, return the length of the longest subarray common to both arrays .
输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。
public int findLength(int[] nums1, int[] nums2) {
int n = nums1.length, m = nums2.length;
// dp[i][j] - nums1[i] 和 nums2[j] 结尾的最长重复子数组的长度
int[][] dp = new int[n + 1][m + 1];
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; 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;
}
longest common subsequence
Given two strings text1
and text2
, return the length of the longest common . Returns if no common0
.
public int longestCommonSubsequence(String s1, String s2) {
int n = s1.length(), m = s2.length();
char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
// dp[i][j] s1 中 [0..i] 和 s2 中 [0..j] 的公共子序列长度
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (cs1[i - 1] == cs2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[n][m];
}
disjoint lines
Write the integers in nums1
and nums2
.
Now, it is possible to draw nums1[i]
some nums2[j]
lines connecting two numbers and , these lines need to satisfy both:
nums1[i] == nums2[j]
- And the line drawn does not intersect any other line (non-horizontal line).
Note that lines cannot intersect even at endpoints: each number can only belong to one line.
Draws lines in this way and returns the maximum number of lines that can be drawn.
输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。
Analyzing the meaning of the question, I found that this question is to find the longest common subsequence of two arrays :
var maxUncrossedLines = function (nums1, nums2) {
const [n, m] = [nums1.length, nums2.length];
let dp = new Array(n + 1).fill(0).map((x) => new Array(m + 1).fill(0));
for (let i = 1; i <= n; i++)
for (let j = 1; j <= m; j++)
if (nums1[i - 1] === nums2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
return dp[n][m];
};
largest subarray sum
Given an array of integers nums
, please find a continuous subarray with the maximum sum (the subarray contains at least one element), and return its maximum sum.
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
public int maxSubArray(int[] nums) {
// dp[i] 以 i 位置结尾的元素的连续子数组的最大和
int[] dp = new int[nums.length];
int res = dp[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
res = Math.max(res, dp[i]);
}
return res;
}
edit distance
judgment subsequence
Given strings s and t , determine whether s is a subsequence of t .
输入:s = "abc", t = "ahbgdc"
输出:true
Double pointer:
public boolean isSubsequence(String s, String t) {
if (s.length() == 0) return true;
char [] cs = s.toCharArray(), ct = t.toCharArray();
int j = 0;
for (int i = 0; i < ct.length; i++) {
if (cs[j] == ct[i]) j++;
if (j == cs.length) return true;
}
return false;
}
DP:
public boolean isSubsequence(String s, String t) {
int l1 = s.length(), l2 = t.length();
// dp[i][j] s 中 [0..i] 的串和 t 中 [0..j] 的串的公共子序列的长度
int[][] dp = new int[l1 + 1][l2 + 1];
for (int i = 1; i <= l1; i++)
for (int j = 1; j <= l2; j++)
if (s.charAt(i - 1) == t.charAt(j - 1))
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = dp[i][j - 1];
return dp[l1][l2] == s.length();
}
different subsequences
Given a string s
and a string t
, s
count t
the number of occurrences of in a subsequence of .
dp[i][j]: s[0..i] 和 t[0..j] 的子序列的个数
s[i] == t[j] 的情况:
- 对于 s = "rara", t = "ra", i = 3, j = 1
1. s 用最后一位 'a', 此时相当于求 "rar" 和 "r" 的子序列个数, 即 dp[i - 1][j - 1]
2. s 不用最后一位 'a', 此时相当于求 "rar" 和 "ra" 的子序列个数, 即 dp[i - 1][j]
s[i] != t[j] 的情况:
- 对于 s = "rarb", t = "ra", i = 3, j = 1
1. s 只能不用最后一位 'b', 此时相当于求 "rar" 和 "ra" 的子序列个数, 即 dp[i - 1][j]
public int numDistinct(String s, String t) {
int n = s.length(), m = t.length();
char[] cs = s.toCharArray(), ct = t.toCharArray();
// dp[i][j] s[0..i] 和 t[0..j] 的子序列的个数
int[][] dp = new int[n + 1][m + 1];
for (int i = 0; i <= n; i++) dp[i][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (j > i) continue;
if (cs[i - 1] == ct[j - 1])
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[n][m];
}
Delete operation for two strings
Given two words word1
and word2
, word1
return the minimum number of steps required to make and word2
equal . Each step can delete one character in any string.
输入: word1 = "sea", word2 = "eat"
输出: 2
解释: 第一步将 "sea" 变为 "ea" ,第二步将 "eat "变为 "ea"
DP 1: (edit distance idea)
public int minDistance(String s1, String s2) {
char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
int n = s1.length(), m = s2.length();
// dp[i][j] 删除字符使得 s1 [0..i] 和 s2 [0..j] 相同的最小步数
int[][] dp = new int[n + 1][m + 1];
for (int i = 0; i <= n; i++) dp[i][0] = i;
for (int i = 0; i <= m; i++) dp[0][i] = i;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (cs1[i - 1] == cs2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = Math.min(dp[i - 1][j - 1] + 2,
Math.min(dp[i - 1][j], dp[i][j - 1]) + 1);
return dp[n][m];
}
DP2: Find the length of the longest common subsequence of two strings, and all other characters need to be deleted
public int minDistance(String s1, String s2) {
int n = s1.length(), m = s2.length();
char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
// dp[i][j] s1 [0..i] 和 s2 [0..j] 的最长公共子序列的长度
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (cs1[i - 1] == cs2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
return m + n - dp[n][m] * 2;
}
edit distance
Title: 72. Edit Distance - LeetCode
Given your two words word1
and word2
, return the minimum number of operations used toword1
word2
convert from to .
You can perform the following three operations on a word:
- insert a character
- delete a character
- replace a character
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
public int minDistance(String s1, String s2) {
int n = s1.length(), m = s2.length();
char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray();
// dp[i][j] 将 s1[0..n] 变成 s2[0..m] 需要的最少操作数
int[][] dp = new int[n + 1][m + 1];
for (int i = 0; i <= n; i++) dp[i][0] = i;
for (int i = 0; i <= m; i++) dp[0][i] = i;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
// 不需要进行操作
if (cs1[i - 1] == cs2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
// 需要进行操作: 取 添加, 删除, 修改 的最小值
else
dp[i][j] = Math.min(
Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]
) + 1;
return dp[n][m];
}
palindrome skewer
palindrome skewer
Given a string s
, please count and return the number of palindromic substrings in this string.
输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
Two-dimensional DP:
public int countSubstrings(String s) {
char[] cs = s.toCharArray();
// dp[i][j] [i..j] 的子串是否是回文串
boolean[][] dp = new boolean[s.length()][s.length()];
int res = 0;
for (int i = cs.length - 1; i >= 0; i--)
for (int j = i; j < cs.length; j++)
// 由状态转移方程可知, 每一个位置的更新取决于左下角位置的状态值
// 所以外层循环从下往上, 内层从左往右
if (cs[i] == cs[j] && (j - i < 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
res++;
}
return res;
}
Center spread:
class Solution {
int res = 0;
public int countSubstrings(String s) {
char[] cs = s.toCharArray();
for (int i = 0; i < cs.length; i++) {
count(cs, i, i); // 回文串长度为奇数
count(cs, i, i + 1); // 回文串长度为偶数
}
return res;
}
void count(char[] cs, int l, int r) {
while (l >= 0 && r < cs.length && cs[l] == cs[r]) {
res++;
l--;
r++;
}
}
}
One-dimensional DP: (violent double pointer)
class Solution {
public int countSubstrings(String s) {
char[] cs = s.toCharArray();
// dp[i] 以 s[i] 结尾的回文子串的数目
int[] dp = new int[cs.length];
for (int i = 0; i < cs.length; i++)
for (int j = 0; j <= i; j++)
if (isPalindrome(cs, j, i))
dp[i]++;
return Arrays.stream(dp).sum();
}
// 是否是回文串
boolean isPalindrome(char[] cs, int l, int r) {
while (l < r)
if (cs[l++] != cs[r--])
return false;
return true;
}
}
longest palindromic substring
Given a string s
, find the longest palindromic substring s
in .
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
Center spread:
class Solution {
String res = "";
public String longestPalindrome(String s) {
for (int i = 0; i < s.length(); i++) {
helper(s, i, i); // 处理奇数长度的回文串
helper(s, i, i + 1); // 处理偶数长度的回文串
}
return res;
}
void helper(String s, int l, int r) {
while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
String t = s.substring(l, r + 1);
if (t.length() > res.length()) res = t;
l--;
r++;
}
}
}
Dynamic programming:
public String longestPalindrome(String s) {
// dp[i][j] s[i..j] 的子串是否是回文子串
boolean[][] dp = new boolean[s.length()][s.length()];
int l = 0, r = 0; // 记录结果的截取位置
for (int i = s.length() - 1; i >= 0; i--)
for (int j = i; j < s.length(); j++)
// 由状态转移方程可知, 每一个位置的更新取决于左下角位置的状态值
// 所以外层循环从下往上, 内层从左往右
if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
if (r - l < j - i) {
l = i;
r = j;
}
}
return s.substring(l, r + 1);
}
longest palindromic subsequence
Given a string s
, find the longest palindromic subsequence in it and return the length of that sequence.
输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。
public int longestPalindromeSubseq(String s) {
int n = s.length();
char[] cs = s.toCharArray();
// dp[i][j] s[i..j] 中的最长回文子序列的长度
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++) dp[i][i] = 1;
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
if (cs[i] == cs[j])
dp[i][j] = dp[i + 1][j - 1] + 2;
else
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
}
return dp[0][n - 1];
}