leecode更新
This commit is contained in:
parent
4d3148c403
commit
f7ccdf71e5
|
|
@ -1,5 +1,7 @@
|
||||||
package com.markilue.leecode.dynamic.second;
|
package com.markilue.leecode.dynamic.second;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*@BelongsProject: Leecode
|
*@BelongsProject: Leecode
|
||||||
*@BelongsPackage: com.markilue.leecode.dynamic.second
|
*@BelongsPackage: com.markilue.leecode.dynamic.second
|
||||||
|
|
@ -16,6 +18,14 @@ package com.markilue.leecode.dynamic.second;
|
||||||
*/
|
*/
|
||||||
public class T09_LastStoneWeightII {
|
public class T09_LastStoneWeightII {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
String str1 = new String("1") + new String("1");
|
||||||
|
str1.intern();
|
||||||
|
String str2 = "11";
|
||||||
|
System.out.println(str1 == str2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 思路:消石头可以理解为总有一些是用来消另一些的,那么可以变为让一堆+去消另一堆-
|
* 思路:消石头可以理解为总有一些是用来消另一些的,那么可以变为让一堆+去消另一堆-
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
package com.markilue.leecode.dynamic.second;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*@BelongsProject: Leecode
|
||||||
|
*@BelongsPackage: com.markilue.leecode.dynamic.second
|
||||||
|
*@Author: markilue
|
||||||
|
*@CreateTime: 2023-02-16 10:20
|
||||||
|
*@Description:
|
||||||
|
* TODO 力扣394题 目标和:
|
||||||
|
* 给你一个整数数组 nums 和一个整数 target 。
|
||||||
|
* 向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
|
||||||
|
* 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
|
||||||
|
* 返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
|
||||||
|
* 1 <= nums.length <= 20
|
||||||
|
* 0 <= nums[i] <= 1000
|
||||||
|
* 0 <= sum(nums[i]) <= 1000
|
||||||
|
* -1000 <= target <= 1000
|
||||||
|
*@Version: 1.0
|
||||||
|
*/
|
||||||
|
public class T10_FindTargetSumWays {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
int[] nums = {100};
|
||||||
|
int target = -200;
|
||||||
|
System.out.println(findTargetSumWays(nums, target));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 思路:可以计算出sum出来,然后有正的一半,有负的一半,那么x-(sum-x)=target;x=(sum+target)/2;所以又转为了背包问题
|
||||||
|
* TODO DP五部曲:
|
||||||
|
* 1.dp定义:dp[i][j]表示使用nums[0-i]能凑出j的方法数
|
||||||
|
* 2.dp状态转移方程:
|
||||||
|
* 1.dp[i][j]有两种方式得到:
|
||||||
|
* 1)使用当前的数nums[i]: dp[i-1][j-nums[i]]
|
||||||
|
* 2)不使用当前的数nums[i]: dp[i-1][j]
|
||||||
|
* dp[i][j]=dp[i-1][j-nums[i]]+dp[i-1][j]
|
||||||
|
* 3.dp初始化:dp[i][0]=1 dp[0][j==nums[0]]=1
|
||||||
|
* 4.dp遍历顺序:从上到下 从前往后
|
||||||
|
* 5.dp举例推导:
|
||||||
|
* 速度击败67.3% 内存击败12%
|
||||||
|
* @param nums
|
||||||
|
* @param target
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int findTargetSumWays(int[] nums, int target) {
|
||||||
|
|
||||||
|
// if (nums.length == 1) {
|
||||||
|
// return nums[0] == target ? 1 : 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
int sum = 0;
|
||||||
|
for (int num : nums) {
|
||||||
|
sum += num;
|
||||||
|
}
|
||||||
|
if ((sum + target) % 2 != 0) return 0;
|
||||||
|
if ((sum + target) < 0) return 0;//注意条件:-1000 <= target <= 1000
|
||||||
|
int need = (sum + target) / 2;
|
||||||
|
|
||||||
|
int[][] dp = new int[nums.length][need + 1];
|
||||||
|
|
||||||
|
//初始化
|
||||||
|
for (int i = 0; i < dp.length; i++) {
|
||||||
|
dp[i][0] = 1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < dp[0].length; i++) {
|
||||||
|
if (i == nums[0]) {
|
||||||
|
dp[0][i] += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < dp.length; i++) {
|
||||||
|
for (int j = 0; j < dp[0].length; j++) {
|
||||||
|
if (j >= nums[i]) dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j];
|
||||||
|
else dp[i][j] = dp[i - 1][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[dp.length - 1][need];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一维dp优化
|
||||||
|
* 速度击败94.6% 内存击败42.53% 2ms
|
||||||
|
* @param nums
|
||||||
|
* @param target
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int findTargetSumWays1(int[] nums, int target) {
|
||||||
|
|
||||||
|
int sum = 0;
|
||||||
|
for (int num : nums) {
|
||||||
|
sum += num;
|
||||||
|
}
|
||||||
|
if ((sum + target) % 2 != 0) return 0;
|
||||||
|
if ((sum + target) < 0) return 0;//注意条件:-1000 <= target <= 1000
|
||||||
|
int need = (sum + target) / 2;
|
||||||
|
|
||||||
|
int[] dp = new int[need + 1];
|
||||||
|
|
||||||
|
//初始化
|
||||||
|
dp[0] = 1;
|
||||||
|
for (int i = 0; i < dp.length; i++) {
|
||||||
|
if (i == nums[0]) {
|
||||||
|
dp[i] += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < nums.length; i++) {
|
||||||
|
for (int j = dp.length - 1; j >= nums[i]; j--) {
|
||||||
|
dp[j] = dp[j - nums[i]] + dp[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[need];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
package com.markilue.leecode.dynamic.second;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*@BelongsProject: Leecode
|
||||||
|
*@BelongsPackage: com.markilue.leecode.dynamic.second
|
||||||
|
*@Author: markilue
|
||||||
|
*@CreateTime: 2023-02-16 10:58
|
||||||
|
*@Description:
|
||||||
|
* TODO 力扣474题 一和零:
|
||||||
|
* 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
|
||||||
|
* 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
|
||||||
|
* 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
|
||||||
|
*@Version: 1.0
|
||||||
|
*/
|
||||||
|
public class T11_FindMaxForm {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
String[] strs = {"10", "0", "1"};
|
||||||
|
int m = 1, n = 1;
|
||||||
|
System.out.println(findMaxForm(strs, m, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 思路:可以看做是有两个限制的背包:
|
||||||
|
* TODO DP五部曲:
|
||||||
|
* 1.dp定义:dp[i][j][k]表示使用strs[0-i]而背包大小为j和k时,最多的数量
|
||||||
|
* 2.dp状态转移方程:
|
||||||
|
* 1.dp[i][j][k]可以使用str[i]或者不适用str[i]
|
||||||
|
* dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-str[i 0]][k-str[i `0`]]+1)
|
||||||
|
* 3.dp初始化:dp[i][0][k]=0;dp[i][j][0]=0;dp[0][j][k]=1;
|
||||||
|
* 4.dp遍历顺序:
|
||||||
|
* 速度击败6.33% 内存击败5.2% 64ms
|
||||||
|
* @param strs
|
||||||
|
* @param m
|
||||||
|
* @param n
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int findMaxForm(String[] strs, int m, int n) {
|
||||||
|
|
||||||
|
//记录每个zero,one个数
|
||||||
|
int[][] nums = new int[strs.length][2];
|
||||||
|
int count = -1;
|
||||||
|
for (String str : strs) {
|
||||||
|
int zero = 0;
|
||||||
|
int one = 0;
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (c == '0') zero++;
|
||||||
|
else one++;
|
||||||
|
}
|
||||||
|
nums[++count] = new int[]{zero, one};
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化
|
||||||
|
int[][][] dp = new int[strs.length][m + 1][n + 1];
|
||||||
|
for (int i = nums[0][0]; i < m + 1; i++) {
|
||||||
|
for (int j = nums[0][1]; j < n + 1; j++) {
|
||||||
|
dp[0][i][j] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 1; i < dp.length; i++) {
|
||||||
|
for (int j = 0; j < dp[0].length; j++) {
|
||||||
|
if (j < nums[i][0]) dp[i][j] = dp[i - 1][j];
|
||||||
|
else {
|
||||||
|
for (int k = 0; k < dp[0][0].length; k++) {
|
||||||
|
if (k < nums[i][1]) 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 - nums[i][0]][k - nums[i][1]] + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[dp.length - 1][m][n];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维dp优化
|
||||||
|
* 速度击败30.23% 内存击败98.69% 27ms
|
||||||
|
* @param strs
|
||||||
|
* @param m
|
||||||
|
* @param n
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int findMaxForm1(String[] strs, int m, int n) {
|
||||||
|
|
||||||
|
//记录每个zero,one个数
|
||||||
|
int[][] nums = new int[strs.length][2];
|
||||||
|
int count = -1;
|
||||||
|
for (String str : strs) {
|
||||||
|
int zero = 0;
|
||||||
|
int one = 0;
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
if (c == '0') zero++;
|
||||||
|
else one++;
|
||||||
|
}
|
||||||
|
nums[++count] = new int[]{zero, one};
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化
|
||||||
|
int[][] dp = new int[m + 1][n + 1];
|
||||||
|
for (int i = nums[0][0]; i < m + 1; i++) {
|
||||||
|
for (int j = nums[0][1]; j < n + 1; j++) {
|
||||||
|
dp[i][j] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 1; i < strs.length; i++) {
|
||||||
|
for (int j = dp.length - 1; j >= nums[i][0]; j--) {
|
||||||
|
for (int k = dp[0].length - 1; k >= nums[i][1]; k--) {
|
||||||
|
dp[j][k] = Math.max(dp[j][k], dp[j - nums[i][0]][k - nums[i][1]] + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[m][n];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进一步优化,少for循环求nums一次
|
||||||
|
* 速度击败97.12% 内存击败49.46% 16ms
|
||||||
|
* @param strs
|
||||||
|
* @param m
|
||||||
|
* @param n
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int findMaxForm2(String[] strs, int m, int n) {
|
||||||
|
|
||||||
|
int[][] dp = new int[m + 1][n + 1];
|
||||||
|
int length = strs.length;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int[] zerosOnes = getZerosOnes(strs[i]);
|
||||||
|
int zeros = zerosOnes[0], ones = zerosOnes[1];
|
||||||
|
for (int j = m; j >= zeros; j--) {
|
||||||
|
for (int k = n; k >= ones; k--) {
|
||||||
|
dp[j][k] = Math.max(dp[j][k], dp[j - zeros][k - ones] + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[m][n];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getZerosOnes(String str) {
|
||||||
|
int[] zerosOnes = new int[2];
|
||||||
|
int length = str.length();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
zerosOnes[str.charAt(i) - '0']++;
|
||||||
|
}
|
||||||
|
return zerosOnes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 官方最快:回溯
|
||||||
|
* 速度击败100% 内存击败99.84% 6ms
|
||||||
|
*/
|
||||||
|
int max = 0;
|
||||||
|
public int findMaxForm3(String[] strs, int m, int n) {
|
||||||
|
int len = strs.length;
|
||||||
|
int[][] counts = new int[len][2];
|
||||||
|
for(int i = 0; i < len; i++) {
|
||||||
|
counts[i] = count(strs[i]);
|
||||||
|
}
|
||||||
|
Arrays.sort(counts, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
|
||||||
|
backTrace(counts, 0, m, n, 0);
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backTrace(int[][] counts, int index, int m, int n, int currLength) {
|
||||||
|
if(m < 0 || n < 0) return;
|
||||||
|
for(int i = index; i < counts.length; i++) {
|
||||||
|
int[] count = counts[i];
|
||||||
|
if(i > index && count[0] == counts[i-1][0]) continue;
|
||||||
|
if(count[0] <= m && count[1] <= n) {
|
||||||
|
max = Math.max(max, currLength + 1);
|
||||||
|
backTrace(counts, i + 1, m - count[0], n - count[1], currLength + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] count(String s) {
|
||||||
|
int oneCount = 0;
|
||||||
|
int zeroCount = 0;
|
||||||
|
int i = s.length() - 1;
|
||||||
|
while(i >= 0) {
|
||||||
|
char sc = s.charAt(i);
|
||||||
|
if(sc == '1') {
|
||||||
|
oneCount++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
zeroCount++;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
return new int[]{zeroCount,oneCount};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.markilue.leecode.dynamic.second;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*@BelongsProject: Leecode
|
||||||
|
*@BelongsPackage: com.markilue.leecode.dynamic.second
|
||||||
|
*@Author: markilue
|
||||||
|
*@CreateTime: 2023-02-16 11:54
|
||||||
|
*@Description:
|
||||||
|
* TODO 力扣518题 零钱兑换II:
|
||||||
|
* 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
|
||||||
|
* 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
|
||||||
|
* 假设每一种面额的硬币有无限个。
|
||||||
|
* 题目数据保证结果符合 32 位带符号整数。
|
||||||
|
*@Version: 1.0
|
||||||
|
*/
|
||||||
|
public class T12_Change {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
int amount = 500;
|
||||||
|
int[] coins = {2, 7, 13};
|
||||||
|
System.out.println(change(amount, coins));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 思路:完全背包问题 -> 可以重复使用coins
|
||||||
|
* TODO DP五部曲:
|
||||||
|
* 1.dp含义:dp[i][j]表示使用coins[0-i]能凑出coins的方式
|
||||||
|
* 2.dp状态转移方程:dp[i][j]有两种方式凑出 : 使用coins[i] 不使用coins[i]
|
||||||
|
* dp[i][j]=dp[i-1][j]+dp[i][j-coins[i]]
|
||||||
|
* 3.dp初始化:dp[i][0]=1 dp[0][j%coins[0]==0]=1
|
||||||
|
* 4.dp遍历顺序:先遍历coins,再遍历背包:因为求的是组合数
|
||||||
|
* 5.dp举例推导:
|
||||||
|
* 速度击败26.38% 内存击败19.51%
|
||||||
|
* @param amount
|
||||||
|
* @param coins
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int change(int amount, int[] coins) {
|
||||||
|
|
||||||
|
int[][] dp = new int[coins.length][amount + 1];
|
||||||
|
|
||||||
|
for (int i = 0; i < dp.length; i++) {
|
||||||
|
dp[i][0] = 1;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < dp[0].length; i++) {
|
||||||
|
if (i % coins[0] == 0) dp[0][i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < dp.length; i++) {
|
||||||
|
for (int j = 1; j < dp[0].length; j++) {
|
||||||
|
if (j < coins[i]) dp[i][j] = dp[i - 1][j];
|
||||||
|
else dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[coins.length - 1][amount];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一维dp优化
|
||||||
|
* 速度击败41.89% 内存击败54.24%
|
||||||
|
* @param amount
|
||||||
|
* @param coins
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int change1(int amount, int[] coins) {
|
||||||
|
|
||||||
|
int[] dp = new int[amount + 1];
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < dp.length; i++) {
|
||||||
|
if (i % coins[0] == 0) dp[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < coins.length; i++) {
|
||||||
|
for (int j = 1; j < dp.length; j++) {
|
||||||
|
if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dp[amount];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue