modify code

master
algorithmzuo 1 year ago
parent 4c12cfd6d5
commit f31e8a8eb5

@ -0,0 +1,67 @@
package class_2023_06_2_week;
// 给你一个长度为 n 下标从 0 开始的整数数组 nums
// 它包含 1 到 n 的所有数字,请你返回上升四元组的数目。
// 如果一个四元组 (i, j, k, l) 满足以下条件,我们称它是上升的:
// 0 <= i < j < k < l < n 且
// nums[i] < nums[k] < nums[j] < nums[l] 。
// 测试链接 : https://leetcode.cn/problems/count-increasing-quadruplets/
public class Code01_CountIncreasingQuadruplets {
// 非常强的思路和实现
public static long countQuadruplets1(int[] nums) {
int n = nums.length;
long ans = 0;
// dp[j]含义 :
// ............................l
// 目前假设刚来到l位置那么在l之前的范围上
// 位置 : 0....i....j....k....l-1
// 如果j做中间点请问有多少三元组满足 : arr[i] < arr[k] < arr[j]
// 就是 : 小 大(j位置的数) 中
// 这种三元组的数量就是dp[j]的含义
long[] dp = new long[n];
for (int l = 1; l < n; l++) {
// 5 9
// j l
// 0 1 2 3 4 5 6 ....l-1
for (int j = 0; j < l; j++) {
if (nums[j] < nums[l]) {
ans += dp[j];
}
}
// dp[0...l-1]上的所有信息,有效的范围 : 0 .... l-1
// dp[0...l-1],扩充有效范围: 0........l
// 目前比[l]数小的数的个数
int cnt = 0;
for (int j = 0; j < l; j++) {
if (nums[j] < nums[l]) {
cnt++;
} else {
dp[j] += cnt;
}
}
}
return ans;
}
// 非常强的思路和实现,彻底看不懂版,其实就是上面的代码做了逻辑合并
public static long countQuadruplets2(int[] nums) {
int n = nums.length;
long ans = 0;
long[] dp = new long[n];
for (int l = 1; l < n; l++) {
int cnt = 0;
for (int j = 0; j < l; j++) {
if (nums[j] < nums[l]) {
ans += dp[j];
cnt++;
} else {
dp[j] += cnt;
}
}
}
return ans;
}
}

@ -0,0 +1,183 @@
package class_2023_06_2_week;
// 来自华为od
// 给定一个数组arr长度为n表示n个格子的分数并且这些格子首尾相连
// 孩子不能选相邻的格子,不能回头选,不能选超过一圈
// 但是孩子可以决定从任何位置开始选,也可以什么都不选
// 返回孩子能获得的最大分值
// 1 <= n <= 10^6
// 0 <= arr[i] <= 10^6
public class Code02_JumpGameOnLoop {
// 暴力方法
// 为了测试
public static int max1(int[] arr) {
if (arr.length == 1) {
return arr[0];
}
return process(arr, 0, new boolean[arr.length]);
}
public static int process(int[] arr, int i, boolean[] path) {
if (i == arr.length) {
if (path[0] && path[arr.length - 1]) {
return Integer.MIN_VALUE;
}
for (int j = 1; j < arr.length; j++) {
if (path[j - 1] && path[j]) {
return Integer.MIN_VALUE;
}
}
int ans = 0;
for (int j = 0; j < arr.length; j++) {
if (path[j]) {
ans += arr[j];
}
}
return ans;
} else {
path[i] = true;
int ans1 = process(arr, i + 1, path);
path[i] = false;
int ans2 = process(arr, i + 1, path);
return Math.max(ans1, ans2);
}
}
// 时间复杂度O(N),记忆化搜索
public static int max2(int[] arr) {
if (arr.length == 1) {
return arr[0];
}
int n = arr.length;
int[][] dp = new int[n][4];
for (int i = 0; i < n; i++) {
dp[i][0] = Integer.MIN_VALUE;
dp[i][1] = Integer.MIN_VALUE;
dp[i][2] = Integer.MIN_VALUE;
dp[i][3] = Integer.MIN_VALUE;
}
// 可能性1:
// 拿0位置的数 : arr[0] + process2(arr, 1, 1, 1, dp);
int ans = arr[0] + process2(arr, 1, 1, 1, dp);
// 可能性:
// 不拿0位置的数 : process2(arr, 1, 0, 0, dp)
ans = Math.max(ans, process2(arr, 1, 0, 0, dp));
return ans;
}
// arr[0....n-1]注意0和n-1是相邻的
// 当前来到的位置是i, 要,不要
// pre i位置的前一个数
// pre == 1i位置的前一个数已经拿了
// pre == 0i位置的前一个数已经没拿
// end == 1说明: 有朝一日来到n-1位置的话不能拿
// end == 1说明: 有朝一日来到n-1位置的话可以考虑
// 返回值 : 在上面的设定下i....n-1能获得的最大累加和是多少
// 注意 : 不能拿相邻数字
public static int process2(int[] arr, int i, int pre, int end, int[][] dp) {
if (i == arr.length - 1) {
// 来到n-1位置了!
return (pre == 1 || end == 1) ? 0 : Math.max(0, arr[i]);
} else {
if (dp[i][(pre << 1) | end] != Integer.MIN_VALUE) {
return dp[i][(pre << 1) | end];
}
int p1 = process2(arr, i + 1, 0, end, dp);
int p2 = Integer.MIN_VALUE;
if (pre != 1) {
p2 = arr[i] + process2(arr, i + 1, 1, end, dp);
}
int ans = Math.max(p1, p2);
dp[i][(pre << 1) | end] = ans;
return ans;
}
}
// i : 0~n-1
// pre : 0 1
// end : 0 1
// int[][][] dp = new int[n][2][2];
// int[][] dp = new int[n][4];
// pre == 0 , end == 0 -> 0
// pre == 0 , end == 1 -> 1
// pre == 1 , end == 0 -> 2
// pre == 1 , end == 1 -> 3
// pre , end -> (pre << 1) | end
public static int zuo(int[] arr, int i, int pre, int end) {
if (i == arr.length - 1) {
// 来到n-1位置了!
return (pre == 1 || end == 1) ? 0 : Math.max(0, arr[i]);
} else {
// 没到最后
// 可能性1 : 不要i位置的数
// i+1, 0, end
int p1 = zuo(arr, i + 1, 0, end);
// 可能性2 : 要i位置的数
int p2 = Integer.MIN_VALUE;
if (pre != 1) {
p2 = arr[i] + zuo(arr, i + 1, 1, end);
}
int ans = Math.max(p1, p2);
return ans;
}
}
// 正式方法
// 严格位置依赖的动态规划 + 空间压缩
// 时间复杂度O(N)
public static int max3(int[] arr) {
if (arr.length == 1) {
return arr[0];
}
int n = arr.length;
int[] next = new int[4];
int[] cur = new int[4];
next[0] = Math.max(0, arr[n - 1]);
for (int i = n - 2; i >= 1; i--) {
for (int pre = 0; pre < 2; pre++) {
for (int end = 0; end < 2; end++) {
cur[(pre << 1) | end] = next[end];
if (pre != 1) {
cur[(pre << 1) | end] = Math.max(cur[(pre << 1) | end], arr[i] + next[2 + end]);
}
}
}
next[0] = cur[0];
next[1] = cur[1];
next[2] = cur[2];
next[3] = cur[3];
}
return Math.max(arr[0] + next[3], next[0]);
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = (int) (Math.random() * v);
}
return arr;
}
// 为了测试
public static void main(String[] args) {
int N = 16;
int V = 100;
int testTimes = 5000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[] arr = randomArray(n, V);
int ans1 = max1(arr);
int ans2 = max2(arr);
int ans3 = max3(arr);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,41 @@
package class_2023_06_2_week;
// 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次
// 需保证 返回结果的字典序最小
// 要求不能打乱其他字符的相对位置)
// 测试链接 : https://leetcode.cn/problems/remove-duplicate-letters/
// 大厂刷题班讲过,不过那时没有讲出最优解,安排一下重讲
public class Code03_RemoveDuplicateLetters {
public static String removeDuplicateLetters(String s) {
// 统计每一种字符,出现的词频
// 为了判断,某一种字符,后面还存在不存在
int[] cnts = new int[26];
// 判断某一种字符,是不是已经在栈里了
boolean[] enter = new boolean[26];
for (int i = 0; i < s.length(); i++) {
cnts[s.charAt(i) - 'a']++;
}
// 栈 26长度足够
char[] stack = new char[26];
int size = 0;
for (int i = 0; i < s.length(); i++) {
char cur = s.charAt(i);
if (!enter[cur - 'a']) {
// 大压小,讨论要不要让栈顶字符,走!
while (
size > 0
&& stack[size - 1] > cur
&& cnts[stack[size - 1] - 'a'] > 0) {
enter[stack[size - 1] - 'a'] = false;
size--;
}
stack[size++] = cur;
enter[cur - 'a'] = true;
}
cnts[cur - 'a']--;
}
return String.valueOf(stack, 0, size);
}
}

@ -0,0 +1,46 @@
package class_2023_06_2_week;
import java.util.Arrays;
// 给你一个由 n 个数对组成的数对数组 pairs
// 其中 pairs[i] = [lefti, righti] 且 lefti < righti 。
// 现在,我们定义一种 跟随 关系,当且仅当 b < c 时
// 数对 p2 = [c, d] 才可以跟在 p1 = [a, b] 后面
// 我们用这种形式来构造 数对链
// 找出并返回能够形成的 最长数对链的长度
// 你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
// 测试链接 : https://leetcode.cn/problems/maximum-length-of-pair-chain/
public class Code04_MaximumLengthOfPairChain {
public static int findLongestChain(int[][] pairs) {
int n = pairs.length;
Arrays.sort(pairs, (a, b) -> a[0] - b[0]);
int[] ends = new int[n];
int size = 0;
for (int[] pair : pairs) {
int l = 0;
int r = size - 1;
int m = 0;
int find = -1;
while (l <= r) {
m = (l + r) / 2;
if (ends[m] >= pair[0]) {
find = m;
r = m - 1;
} else {
l = m + 1;
}
}
if (find == -1) {
ends[size++] = pair[1];
} else {
// 1,5 1,3 1,8
// 3
// 1
ends[find] = Math.min(ends[find], pair[1]);
}
}
return size;
}
}

@ -0,0 +1,201 @@
package class_2023_06_2_week;
import java.util.Arrays;
// 给你两个整数数组 arr1 和 arr2
// 返回使 arr1 严格递增所需要的最小「操作」数(可能为 0
// 每一步「操作」中,你可以分别从 arr1 和 arr2 中各选出一个索引,
// 分别为 i 和 j0 <= i < arr1.length 和 0 <= j < arr2.length
// 然后进行赋值运算 arr1[i] = arr2[j]。如果无法让 arr1 严格递增,请返回 -1。
// 之前讲过这个题,能通过但不是最优解,所以我们直接补一个最优解
// 测试链接 : https://leetcode.cn/problems/make-array-strictly-increasing/
public class Code05_MakeArrayStrictlyIncreasing {
public static int makeArrayIncreasing1(int[] arr1, int[] arr2) {
// arr2 [3,7,7,2,3]
Arrays.sort(arr2);
// arr2 [2,3,3,7,7]
int cnt = 1;
for (int i = 1; i < arr2.length; i++) {
if (arr2[i] != arr2[i - 1]) {
cnt++;
}
}
// cnt = 3, 去重之后,只有三个数字
int[] help = new int[cnt];
help[0] = arr2[0];
for (int i = 1, j = 1; i < arr2.length; i++) {
if (arr2[i] != arr2[i - 1]) {
help[j++] = arr2[i];
}
}
// help : arr2去重且排序的结果
// 2 3 7
int ans = process1(arr1, help, -1);
return ans == Integer.MAX_VALUE ? -1 : ans;
}
// 系统最小
// -1 0 1 2 3 ...
// arr1[i.....] 在i位置的数一定不换的情况下
// 后续至少要换几个数,能做到整体递增!
public static int process1(int[] arr1, int[] arr2, int i) {
if (i == arr1.length) {
return 0;
} else {
// 没到终止位置
// 当前的数字
// i ==-1 Integer.MIN_VALUE
// i > -1 arr1[i]
// 当前的数字 Integer.MIN_VALUE
// arr2 [ 3, 4, 5, 8, 10]
// 0 1 2 3 4
// f = 0
int cur = i == -1 ? Integer.MIN_VALUE : arr1[i];
// f == -1 >cur的数在arr2中不存在
// f != -1 >cur的数在arr2中最左的位置
int f = find(arr2, cur);
int ans = Integer.MAX_VALUE;
// 目前换了几个数了
int times = 0;
for (int j = i + 1; j <= arr1.length; j++) {
//j 右边离i最近下一个不换的位置
// i(不换) i+1... i+2... i+3...
// i......j(不换)
// cur : 当前来到的j位置的数的前一个数
// cur : 可能被arr2里的数字替过了
if (j == arr1.length || cur < arr1[j]) {
int next = process1(arr1, arr2, j);
if (next != Integer.MAX_VALUE) {
ans = Math.min(ans, times + next);
}
}
if (f != -1 && f < arr2.length) {
cur = arr2[f++];
times++;
} else {
break;
}
}
return ans;
}
}
public static int find(int[] arr2, int num) {
int l = 0;
int r = arr2.length - 1;
int m, ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (arr2[m] > num) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
// 记忆化搜索版
public static int makeArrayIncreasing2(int[] arr1, int[] arr2) {
Arrays.sort(arr2);
int cnt = 1;
for (int i = 1; i < arr2.length; i++) {
if (arr2[i] != arr2[i - 1]) {
cnt++;
}
}
int[] help = new int[cnt];
help[0] = arr2[0];
for (int i = 1, j = 1; i < arr2.length; i++) {
if (arr2[i] != arr2[i - 1]) {
help[j++] = arr2[i];
}
}
int[] dp = new int[arr1.length + 1];
Arrays.fill(dp, -1);
int ans = process2(arr1, help, -1, dp);
return ans == Integer.MAX_VALUE ? -1 : ans;
}
public static int process2(int[] arr1, int[] arr2, int i, int[] dp) {
if (i == arr1.length) {
return 0;
} else {
if (dp[i + 1] != -1) {
return dp[i + 1];
}
int cur = i == -1 ? Integer.MIN_VALUE : arr1[i];
int f = find(arr2, cur);
int ans = Integer.MAX_VALUE;
int times = 0;
for (int j = i + 1; j <= arr1.length; j++) {
if (j == arr1.length || cur < arr1[j]) {
int next = process2(arr1, arr2, j, dp);
if (next != Integer.MAX_VALUE) {
ans = Math.min(ans, times + next);
}
}
if (f != -1 && f < arr2.length) {
cur = arr2[f++];
times++;
} else {
break;
}
}
dp[i + 1] = ans;
return ans;
}
}
// 迭代版的动态规划
public static int makeArrayIncreasing3(int[] arr1, int[] arr2) {
Arrays.sort(arr2);
int m = 1;
for (int i = 1; i < arr2.length; i++) {
if (arr2[i] != arr2[m - 1]) {
arr2[m++] = arr2[i];
}
}
int n = arr1.length;
int[] dp = new int[n + 2];
for (int i = n - 1; i >= -1; i--) {
int cur = i == -1 ? Integer.MIN_VALUE : arr1[i];
int f = find3(arr2, m, cur);
dp[i + 1] = Integer.MAX_VALUE;
int times = 0;
for (int j = i + 1; j <= n; j++) {
if (j == n || cur < arr1[j]) {
if (dp[j + 1] != Integer.MAX_VALUE) {
dp[i + 1] = Math.min(dp[i + 1], times + dp[j + 1]);
}
}
if (f != -1 && f < m) {
cur = arr2[f++];
times++;
} else {
break;
}
}
}
return dp[0] == Integer.MAX_VALUE ? -1 : dp[0];
}
public static int find3(int[] arr2, int size, int num) {
int l = 0;
int r = size - 1;
int m, ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (arr2[m] > num) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
}

@ -3856,4 +3856,64 @@ s' 与 s 匹配,当且仅当 s' 与 s 长度相同,且最多有 k 个位置
测试链接 : https://www.luogu.com.cn/problem/P2601
第072节 2023年6月第2周流行算法题目解析
给你一个长度为 n 下标从 0 开始的整数数组 nums
它包含 1 到 n 的所有数字,请你返回上升四元组的数目。
如果一个四元组 (i, j, k, l) 满足以下条件,我们称它是上升的:
0 <= i < j < k < l < n 且
nums[i] < nums[k] < nums[j] < nums[l] 。
测试链接 : https://leetcode.cn/problems/count-increasing-quadruplets/
来自华为od
给定一个数组arr长度为n表示n个格子的分数并且这些格子首尾相连
孩子不能选相邻的格子,不能回头选,不能选超过一圈
但是孩子可以决定从任何位置开始选,也可以什么都不选
返回孩子能获得的最大分值
1 <= n <= 10^6
0 <= arr[i] <= 10^6
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次
需保证 返回结果的字典序最小
要求不能打乱其他字符的相对位置)
测试链接 : https://leetcode.cn/problems/remove-duplicate-letters/
大厂刷题班讲过,不过那时没有讲出最优解,安排一下重讲
给你一个由 n 个数对组成的数对数组 pairs
其中 pairs[i] = [lefti, righti] 且 lefti < righti 。
现在,我们定义一种 跟随 关系,当且仅当 b < c 时
数对 p2 = [c, d] 才可以跟在 p1 = [a, b] 后面
我们用这种形式来构造 数对链
找出并返回能够形成的 最长数对链的长度
你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
测试链接 : https://leetcode.cn/problems/maximum-length-of-pair-chain/
给你两个整数数组 arr1 和 arr2
返回使 arr1 严格递增所需要的最小「操作」数(可能为 0
每一步「操作」中,你可以分别从 arr1 和 arr2 中各选出一个索引,
分别为 i 和 j0 <= i < arr1.length 和 0 <= j < arr2.length
然后进行赋值运算 arr1[i] = arr2[j]。如果无法让 arr1 严格递增,请返回 -1。
之前讲过这个题,能通过但不是最优解,所以我们直接补一个最优解
测试链接 : https://leetcode.cn/problems/make-array-strictly-increasing/

Loading…
Cancel
Save