From d774ec7d9e409d1c51351fe737e1cffd425c9705 Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Thu, 13 Apr 2023 17:23:49 +0800 Subject: [PATCH] modify code --- .../Code01_GroupBuyTickets.java | 73 ++++ .../Code02_ChemicalProblem.java | 75 ++++ ...e03_StringTransformsIntoAnotherString.java | 38 ++ .../Code04_MinimumKnightMoves.java | 92 +++++ ...LongestChunkedPalindromeDecomposition.java | 356 ++++++++++++++++++ ...养的大厂算法面试题(正在直播) | 73 ++++ 6 files changed, 707 insertions(+) create mode 100644 算法周更班/class_2023_04_2_week/Code01_GroupBuyTickets.java create mode 100644 算法周更班/class_2023_04_2_week/Code02_ChemicalProblem.java create mode 100644 算法周更班/class_2023_04_2_week/Code03_StringTransformsIntoAnotherString.java create mode 100644 算法周更班/class_2023_04_2_week/Code04_MinimumKnightMoves.java create mode 100644 算法周更班/class_2023_04_2_week/Code05_LongestChunkedPalindromeDecomposition.java diff --git a/算法周更班/class_2023_04_2_week/Code01_GroupBuyTickets.java b/算法周更班/class_2023_04_2_week/Code01_GroupBuyTickets.java new file mode 100644 index 0000000..0013af0 --- /dev/null +++ b/算法周更班/class_2023_04_2_week/Code01_GroupBuyTickets.java @@ -0,0 +1,73 @@ +package class_2023_04_2_week; + +import java.util.PriorityQueue; + +// 来自学员问题,来自真实笔试 +// 景区里有m个项目,也就是项目数组为int[][] game,这是一个m*2的二维数组 +// 景区的第i个项目有如下两个参数: +// game[i] = { Ki, Bi } +// Ki一定是负数,Bi一定是正数 +// 举个例子 : +// Ki = -2, Bi = 10 +// 如果只有1个人买票,单张门票的价格为 : Ki * 1 + Bi = 8 +// 所以这1个人游玩该项目要花8元 +// 如果有2个人买票,单张门票的价格为 : Ki * 2 + Bi = 6 +// 所以这2个人游玩该项目要花6 * 2 = 12元 +// 如果有5个人买票,单张门票的价格为 : Ki * 2 + Bi = 0 +// 所以这5个人游玩该项目要花0 * 5 = 0元 +// 如果有更多人买票,都认为花0元(因为你让项目倒贴钱实在是太操蛋了) +// 于是可以认为,如果有x个人买票,单张门票的价格为 : Ki * x + Bi +// x个人游玩这个项目的总花费是 : max { (Ki * x + Bi) * x , 0 } +// 你作为领导,单位一共有n个人,每个人最多可以选1个项目来游玩,也可以不选任何项目 +// 所有员工将在明晚提交选择,然后由你去按照上面的规则,统一花钱,统一购票 +// 但是现在,你想知道自己需要准备多少钱,就可以应付可能的各种情况, +// 支持各种可能下的开销,返回这个最保险的钱数 +// 数据量描述 : +// 1 <= N、M、Bi <= 10^5 +// -(10^5) <= Ki < 0 +public class Code01_GroupBuyTickets { + + // n个人 + // + public static int enoughMoney(int n, int[][] games) { + PriorityQueue heap = new PriorityQueue<>((a, b) -> b.earn() - a.earn()); + // m * log m + for (int[] g : games) { + heap.add(new Game(g[0], g[1])); + } + int ans = 0; + // n * log m + for (int i = 0; i < n; i++) { + if (heap.peek().earn() <= 0) { + break; + } + Game cur = heap.poll(); + ans += cur.earn(); + cur.people++; + heap.add(cur); + } + return ans; + } + + public static class Game { + public int Ki; + public int Bi; + // 已经来了多少人 + // 每来一个人,票价,减少 | K | + public int people; + + public Game(int k, int b) { + Ki = k; + Bi = b; + people = 0; + } + + // 如果再来一个人,景区还能收获多少钱 ? + public int earn() { +// return (Ki * (people + 1) + Bi) + Ki * people; + return (2 * people + 1) * Ki + Bi; + } + + } + +} diff --git a/算法周更班/class_2023_04_2_week/Code02_ChemicalProblem.java b/算法周更班/class_2023_04_2_week/Code02_ChemicalProblem.java new file mode 100644 index 0000000..2297f76 --- /dev/null +++ b/算法周更班/class_2023_04_2_week/Code02_ChemicalProblem.java @@ -0,0 +1,75 @@ +package class_2023_04_2_week; + +import java.util.Arrays; + +// 来自小红书 +// 实验室需要配制一种溶液,现在研究员面前有n种该物质的溶液, +// 每一种有无限多瓶,第i种的溶液体积为v[i],里面含有w[i]单位的该物质 +// 研究员每次可以选择一瓶溶液, +// 将其倒入另外一瓶(假设瓶子的容量无限),即可以看作将两个瓶子内的溶液合并 +// 此时合并的溶液体积和物质含量都等于之前两个瓶子内的之和。 +// 特别地,如果瓶子A与B的溶液体积相同,那么A与B合并之后 +// 该物质的含量会产生化学反应,使得该物质含量增加x单位 +// 研究员的任务是配制溶液体积恰好等于c的,且尽量浓的溶液(即物质含量尽量多) +// 研究员想要知道物质含量最多是多少 +// 对于所有数据,1 <= n, v[i], w[i], x, c <= 1000 +public class Code02_ChemicalProblem { + + public static int maxValue(int[] v, int[] w, int x, int c) { + int n = v.length; + // dp[0] ? dp[1] ? dp[2] ? dp[c] ? + int[] dp = new int[c + 1]; + // dp[i] = -1, 得到i体积,目前为止无方案 + Arrays.fill(dp, -1); + // 天然有的规格,先填一下 + for (int i = 0; i < n; i++) { + // 3 10 dp[3] = 10 + // 5 13 dp[5] = 13 + // 3 20 dp[3] = 20 + if (v[i] <= c) { + dp[v[i]] = Math.max(dp[v[i]], w[i]); + } + } + // 1 ? 2 ? 3 ? c ? dp[1....c] + for (int i = 1; i <= c; i++) { + // i = 10体积 + // 1 + 9 + // 2 + 8 + // 3 + 7 + // 4 + 6 + // 5 + 5 + x + for (int j = 1; j <= i / 2; j++) { + // dp[10] = dp[1] + dp[9] + // 能得到 能得到 + if (dp[j] != -1 && dp[i - j] != -1) { + dp[i] = Math.max(dp[i], + dp[j] + dp[i - j] + (j == i - j ? x : 0)); + } + } + } + return dp[c]; + } + + public static void main(String[] args) { + // 调配溶液 + // 规格0 (体积5,含量2) + // 规格1 (体积3,含量4) + // 规格2 (体积4,含量1) + // 合并双方体积不等的情况下 : + // (体积5,含量2) + (体积4,含量1) = (体积9,含量3) + // 合并双方体积相等的情况下 : + // (体积5,含量5) + (体积5,含量2) = (体积10,含量5 + 2 + x) + // x,额外增加,固定int类参数,x= 10 + // c,一定要得到c体积的溶液,含量最大能是多少 ? + int[] v = { 5, 3, 4 }; + int[] w = { 2, 4, 1 }; + int x = 4; + int c = 16; + // (体积3,含量4) + (体积3,含量4) = (体积6,含量12) + // (体积3,含量4) + (体积3,含量4) = (体积6,含量12) + // (体积6,含量12) + (体积6,含量12) = (体积12,含量28) + // (体积12,含量28) + (体积4,含量1) = (体积16,含量29) + System.out.println(maxValue(v, w, x, c)); + } + +} diff --git a/算法周更班/class_2023_04_2_week/Code03_StringTransformsIntoAnotherString.java b/算法周更班/class_2023_04_2_week/Code03_StringTransformsIntoAnotherString.java new file mode 100644 index 0000000..181cfde --- /dev/null +++ b/算法周更班/class_2023_04_2_week/Code03_StringTransformsIntoAnotherString.java @@ -0,0 +1,38 @@ +package class_2023_04_2_week; + +import java.util.Arrays; + +// 来自谷歌、亚马逊、微软、蔚来、腾讯、字节跳动、Uber +// 给出两个长度相同的字符串 str1 和 str2 +// 请你帮忙判断字符串 str1 能不能在 零次 或 多次 转化 后变成字符串 str2 +// 每一次转化时,你可以将 str1 中出现的 所有 相同字母变成其他 任何 小写英文字母 +// 只有在字符串 str1 能够通过上述方式顺利转化为字符串 str2 时才能返回 true 。​​ +// 测试链接 : https://leetcode.cn/problems/string-transforms-into-another-string/ +public class Code03_StringTransformsIntoAnotherString { + + public static boolean canConvert(String str1, String str2) { + if (str1.equals(str2)) { + return true; + } + int[] map = new int[26]; + int kinds = 0; + for (int i = 0; i < str2.length(); i++) { + if (map[str2.charAt(i) - 'a']++ == 0) { + kinds++; + } + } + if (kinds == 26) { + return false; + } + Arrays.fill(map, -1); + for (int i = 0; i < str1.length(); i++) { + int cur = str1.charAt(i) - 'a'; + if (map[cur] != -1 && str2.charAt(map[cur]) != str2.charAt(i)) { + return false; + } + map[cur] = i; + } + return true; + } + +} diff --git a/算法周更班/class_2023_04_2_week/Code04_MinimumKnightMoves.java b/算法周更班/class_2023_04_2_week/Code04_MinimumKnightMoves.java new file mode 100644 index 0000000..8acbbb8 --- /dev/null +++ b/算法周更班/class_2023_04_2_week/Code04_MinimumKnightMoves.java @@ -0,0 +1,92 @@ +package class_2023_04_2_week; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.PriorityQueue; + +// 来自Indeed、谷歌、亚马逊、领英、英伟达 +// 一个坐标可以从 -infinity 延伸到 +infinity 的 无限大的 棋盘上 +// 你的 骑士 驻扎在坐标为 [0, 0] 的方格里。 +// 骑士的走法和中国象棋中的马相似,走 “日” 字: +// 即先向左(或右)走 1 格,再向上(或下)走 2 格 +// 或先向左(或右)走 2 格,再向上(或下)走 1 格 +// 每次移动,他都可以像中国象棋中的马一样,选八个方向中的一个前进 +// 返回 骑士前去征服坐标为 [x, y] 的部落所需的最小移动次数 +// 本题确保答案是一定存在的。 +// 测试链接 : https://leetcode.cn/problems/minimum-knight-moves/ +public class Code04_MinimumKnightMoves { + + // A*算法的实现 + public static int minKnightMoves(int x, int y) { + + // int[] cur = { + // 0 : 出发点到当前位置跳了几次 + // 1 : 当前位置跳到目标位置,估计还要跳几次 + // 2 : 当前位置的行 + // 3 : 当前位置的列 + // f() : 出发点到当前位置跳了几次,谁次数少,谁先弹出 + // f() + g() : + PriorityQueue heap = new PriorityQueue<>( + (a, b) -> (a[0] + a[1]) - (b[0] + b[1])); + // 从堆里弹出了什么位置,不要重复再考虑了 + HashMap> closed = new HashMap<>(); + heap.add(new int[] { 0, distance(0, 0, x, y), 0, 0 }); + int ans = Integer.MAX_VALUE; + while (!heap.isEmpty()) { + int[] cur = heap.poll(); + int cost = cur[0]; + int row = cur[2]; + int col = cur[3]; + if (isPoped(closed, row, col)) { + continue; + } + if (row == x && col == y) { + ans = cost; + break; + } + close(closed, row, col); + add(cost + 1, row + 2, col + 1, x, y, closed, heap); + add(cost + 1, row + 1, col + 2, x, y, closed, heap); + add(cost + 1, row - 1, col + 2, x, y, closed, heap); + add(cost + 1, row - 2, col + 1, x, y, closed, heap); + add(cost + 1, row - 2, col - 1, x, y, closed, heap); + add(cost + 1, row - 1, col - 2, x, y, closed, heap); + add(cost + 1, row + 1, col - 2, x, y, closed, heap); + add(cost + 1, row + 2, col - 1, x, y, closed, heap); + } + return ans; + } + + // 如果之间弹出过(r,c)点,返回true + // 如果之间没弹出过(r,c)点,返回false + public static boolean isPoped(HashMap> closed, int r, int c) { + return closed.containsKey(r) && closed.get(r).contains(c); + } + + // 把(r,c)点加入closed表 + public static void close(HashMap> closed, int r, int c) { + if (!closed.containsKey(r)) { + closed.put(r, new HashSet<>()); + } + closed.get(r).add(c); + } + + public static void add(int cost, int r, int c, int x, int y, HashMap> closed, + PriorityQueue heap) { + if (!isPoped(closed, r, c)) { + heap.add(new int[] { cost, distance(r, c, x, y), r, c }); + } + } + + // 曼哈顿距离 / 3 + // 为什么要定成这个 + // 因为估计函数的估计代价 要小于等于 真实代价 + // 我们知道,走"日"字是一次蹦3个曼哈顿距离 + // 如果A点到B点的曼哈顿距离是3,不是任意的A和B都能通过走"日"的方式,一步达到的 + // 所以真实代价 >= 曼哈顿距离 / 3 + // 那就把 曼哈顿距离 / 3,定成估计函数 + public static int distance(int r, int c, int x, int y) { + return (Math.abs(x - r) + Math.abs(y - c)) / 3; + } + +} diff --git a/算法周更班/class_2023_04_2_week/Code05_LongestChunkedPalindromeDecomposition.java b/算法周更班/class_2023_04_2_week/Code05_LongestChunkedPalindromeDecomposition.java new file mode 100644 index 0000000..c2d0e9a --- /dev/null +++ b/算法周更班/class_2023_04_2_week/Code05_LongestChunkedPalindromeDecomposition.java @@ -0,0 +1,356 @@ +package class_2023_04_2_week; + +// 来自小红书、谷歌、Bloomberg、微软、亚马逊、字节跳动、摩根大通、Uber +// 你会得到一个字符串 text +// 你应该把它分成 k 个子字符串 (subtext1, subtext2,…, subtextk) +// 要求满足: +// subtexti 是 非空 字符串 +// 所有子字符串的连接等于 text +// ( 即subtext1 + subtext2 + ... + subtextk == text ) +// subtexti == subtextk - i + 1 表示所有 i 的有效值( 即 1 <= i <= k ) +// 返回k可能最大值。 +// 测试链接 : https://leetcode.cn/problems/longest-chunked-palindrome-decomposition/ +public class Code05_LongestChunkedPalindromeDecomposition { + + // 时间复杂度O(N^2) + // 题解上的做法 + // 但这不是最优解 + // 而且没有帖子写出最优解 + public static int longestDecomposition1(String str) { + if (str.length() == 1) { + return 1; + } + char[] s = str.toCharArray(); + int n = s.length; + int l = 0; + int r = n - 1; + int ans = 0; + while (l <= r) { + int size = 1; + for (; 2 * size <= r - l + 1; size++) { + if (same1(s, l, r, size)) { + break; + } + } + if (2 * size <= r - l + 1) { + ans += 2; + } + l += size; + r -= size; + } + return r == l - 1 ? ans : (ans + 1); + } + + public static boolean same1(char[] s, int l, int r, int size) { + for (int i = l, j = r - size + 1; j <= r; i++, j++) { + if (s[i] != s[j]) { + return false; + } + } + return true; + } + + // 时间复杂度O(N * logN) + // 最优解 + // 利用DC3算法生成后缀数组、进而生成高度数组来做的 + // 需要体系学习班,DC3生成后缀数组详解 + // 不过Leetcode的数据量太小,根本显不出最优解的优异 + // 所以我自己写了测试来展示 + public static int longestDecomposition2(String str) { + if (str.length() == 1) { + return 1; + } + char[] s = str.toCharArray(); + int n = s.length; + DC3 dc3 = generateDC3(s, n); + int[] rank = dc3.rank; + RMQ rmq = new RMQ(dc3.height); + int l = 0; + int r = n - 1; + int ans = 0; + while (l <= r) { + int size = 1; + for (; 2 * size <= r - l + 1; size++) { + if (same2(rank, rmq, l, r, size)) { + break; + } + } + if (2 * size <= r - l + 1) { + ans += 2; + } + l += size; + r -= size; + } + return r == l - 1 ? ans : (ans + 1); + } + + public static boolean same2(int[] rank, RMQ rmq, int l, int r, int size) { + int start1 = l; + int start2 = r - size + 1; + int minStart = Math.min(rank[start1], rank[start2]); + int maxStart = Math.max(rank[start1], rank[start2]); + return rmq.min(minStart + 1, maxStart) >= size; + } + + public static DC3 generateDC3(char[] s, int n) { + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (char cha : s) { + min = Math.min(min, cha); + max = Math.max(max, cha); + } + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = s[i] - min + 1; + } + return new DC3(arr, max - min + 1); + } + + public static class DC3 { + public int[] sa; + public int[] rank; + public int[] height; + + public DC3(int[] nums, int max) { + sa = sa(nums, max); + rank = rank(); + height = height(nums); + } + + private int[] sa(int[] nums, int max) { + int n = nums.length; + int[] arr = new int[n + 3]; + for (int i = 0; i < n; i++) { + arr[i] = nums[i]; + } + return skew(arr, n, max); + } + + private int[] skew(int[] nums, int n, int K) { + int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2; + int[] s12 = new int[n02 + 3], sa12 = new int[n02 + 3]; + for (int i = 0, j = 0; i < n + (n0 - n1); ++i) { + if (0 != i % 3) { + s12[j++] = i; + } + } + radixPass(nums, s12, sa12, 2, n02, K); + radixPass(nums, sa12, s12, 1, n02, K); + radixPass(nums, s12, sa12, 0, n02, K); + int name = 0, c0 = -1, c1 = -1, c2 = -1; + for (int i = 0; i < n02; ++i) { + if (c0 != nums[sa12[i]] || c1 != nums[sa12[i] + 1] || c2 != nums[sa12[i] + 2]) { + name++; + c0 = nums[sa12[i]]; + c1 = nums[sa12[i] + 1]; + c2 = nums[sa12[i] + 2]; + } + if (1 == sa12[i] % 3) { + s12[sa12[i] / 3] = name; + } else { + s12[sa12[i] / 3 + n0] = name; + } + } + if (name < n02) { + sa12 = skew(s12, n02, name); + for (int i = 0; i < n02; i++) { + s12[sa12[i]] = i + 1; + } + } else { + for (int i = 0; i < n02; i++) { + sa12[s12[i] - 1] = i; + } + } + int[] s0 = new int[n0], sa0 = new int[n0]; + for (int i = 0, j = 0; i < n02; i++) { + if (sa12[i] < n0) { + s0[j++] = 3 * sa12[i]; + } + } + radixPass(nums, s0, sa0, 0, n0, K); + int[] sa = new int[n]; + for (int p = 0, t = n0 - n1, k = 0; k < n; k++) { + int i = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + int j = sa0[p]; + if (sa12[t] < n0 ? leq(nums[i], s12[sa12[t] + n0], nums[j], s12[j / 3]) + : leq(nums[i], nums[i + 1], s12[sa12[t] - n0 + 1], nums[j], nums[j + 1], s12[j / 3 + n0])) { + sa[k] = i; + t++; + if (t == n02) { + for (k++; p < n0; p++, k++) { + sa[k] = sa0[p]; + } + } + } else { + sa[k] = j; + p++; + if (p == n0) { + for (k++; t < n02; t++, k++) { + sa[k] = sa12[t] < n0 ? sa12[t] * 3 + 1 : (sa12[t] - n0) * 3 + 2; + } + } + } + } + return sa; + } + + private void radixPass(int[] nums, int[] input, int[] output, int offset, int n, int k) { + int[] cnt = new int[k + 1]; + for (int i = 0; i < n; ++i) { + cnt[nums[input[i] + offset]]++; + } + for (int i = 0, sum = 0; i < cnt.length; ++i) { + int t = cnt[i]; + cnt[i] = sum; + sum += t; + } + for (int i = 0; i < n; ++i) { + output[cnt[nums[input[i] + offset]]++] = input[i]; + } + } + + private boolean leq(int a1, int a2, int b1, int b2) { + return a1 < b1 || (a1 == b1 && a2 <= b2); + } + + private boolean leq(int a1, int a2, int a3, int b1, int b2, int b3) { + return a1 < b1 || (a1 == b1 && leq(a2, a3, b2, b3)); + } + + private int[] rank() { + int n = sa.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[sa[i]] = i; + } + return ans; + } + + private int[] height(int[] s) { + int n = s.length; + int[] ans = new int[n]; + for (int i = 0, k = 0; i < n; ++i) { + if (rank[i] != 0) { + if (k > 0) { + --k; + } + int j = sa[rank[i] - 1]; + while (i + k < n && j + k < n && s[i + k] == s[j + k]) { + ++k; + } + ans[rank[i]] = k; + } + } + return ans; + } + + } + + public static class RMQ { + + public int[][] min; + + public RMQ(int[] arr) { + int n = arr.length; + int k = power2(n); + min = new int[n + 1][k + 1]; + for (int i = 1; i <= n; i++) { + min[i][0] = arr[i - 1]; + } + for (int j = 1; (1 << j) <= n; j++) { + for (int i = 1; i + (1 << j) - 1 <= n; i++) { + min[i][j] = Math.min(min[i][j - 1], min[i + (1 << (j - 1))][j - 1]); + } + } + } + + public int min(int l, int r) { + l++; + r++; + int k = power2(r - l + 1); + return Math.min(min[l][k], min[r - (1 << k) + 1][k]); + } + + private int power2(int m) { + int ans = 0; + while ((1 << ans) <= (m >> 1)) { + ans++; + } + return ans; + } + + } + + // 为了测试 + public static String generateS(int a, int b) { + char[] ans = new char[a + b]; + for (int i = 0; i < a; i++) { + ans[i] = 'a'; + } + for (int i = a, j = 0; j < b; i++, j++) { + ans[i] = 'b'; + } + return String.valueOf(ans); + } + + // 为了测试 + public static String generateT(String part, int n) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < n; i++) { + builder.append(part); + } + return builder.toString(); + } + + // 为了测试 + public static void main(String[] args) { + System.out.println("先展示一下DC3的用法"); + String test = "aaabaaa"; + DC3 dc3 = generateDC3(test.toCharArray(), test.length()); + System.out.println("sa[i]表示字典序排名第i的是什么位置开头的后缀串"); + int[] sa = dc3.sa; + for (int i = 0; i < test.length(); i++) { + System.out.println(i + " : " + sa[i]); + } + + System.out.println("rank[i]表示i位置开头的后缀串的字典序排多少名"); + int[] rank = dc3.rank; + for (int i = 0; i < test.length(); i++) { + System.out.println(i + " : " + rank[i]); + } + System.out.println("height[i]表示字典序排名i的后缀串和前一个排名的后缀串,最长公共前缀是多长"); + int[] height = dc3.height; + for (int i = 0; i < test.length(); i++) { + System.out.println(i + " : " + height[i]); + } + + System.out.println("性能测试开始"); + long start, end; + // 构造一个字符串s + // s = a....一共30万个a....ab + String s = generateS(300000, 1); + // 构造总的字符串t + // t = s + s + // t的总长度是60万长度 -> 6 * 10^5 + String t = generateT(s, 2); + + // longestDecomposition1跑完是很慢的 + // 耐心等吧,运行时间在15秒左右 + start = System.currentTimeMillis(); + System.out.println("方法1的结果 : " + longestDecomposition1(t)); + end = System.currentTimeMillis(); + System.out.println("方法1的运行时间 : " + (end - start) + " 毫秒"); + + // longestDecomposition2跑完是很快的 + // 而且你构造不出让longestDecomposition2方法慢的例子 + // 字符串长度在10^6以内,可以随意构造字符串 + // longestDecomposition2方法都会很快 + start = System.currentTimeMillis(); + System.out.println("方法2的结果 : " + longestDecomposition2(t)); + end = System.currentTimeMillis(); + System.out.println("方法2的运行时间 : " + (end - start) + " 毫秒"); + + System.out.println("性能测试结束"); + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 22dbab7..e77c105 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -3371,6 +3371,79 @@ projects[i] = {a, b} +第065节 2023年4月第2周流行算法题目解析 + +来自学员问题,来自真实笔试 +景区里有m个项目,也就是项目数组为int[][] game,这是一个m*2的二维数组 +景区的第i个项目有如下两个参数: +game[i] = { Ki, Bi } Ki一定是负数,Bi一定是正数 +举个例子 : +Ki = -2, Bi = 10 +如果只有1个人买票,单张门票的价格为 : Ki * 1 + Bi = 8 +所以这1个人游玩该项目要花8元 +如果有2个人买票,单张门票的价格为 : Ki * 2 + Bi = 6 +所以这2个人游玩该项目要花6 * 2 = 12元 +如果有5个人买票,单张门票的价格为 : Ki * 2 + Bi = 0 +所以这5个人游玩该项目要花0 * 5 = 0元 +如果有更多人买票,都认为花0元(因为你让项目倒贴钱实在是太操蛋了) +于是可以认为,如果有x个人买票,单张门票的价格为 : Ki * x + Bi +x个人游玩这个项目的总花费是 : max { (Ki * x + Bi) * x , 0 } +你作为领导,单位一共有n个人,每个人最多可以选1个项目来游玩,也可以不选任何项目 +所有员工将在明晚提交选择,然后由你去按照上面的规则,统一花钱,统一购票 +但是现在,你想知道自己需要准备多少钱,就可以应付可能的各种情况, +支持各种可能下的开销,返回这个最保险的钱数 +数据量描述 : +1 <= N、M、Bi <= 10^5 +-(10^5) <= Ki < 0 + +来自小红书 +实验室需要配制一种溶液,现在研究员面前有n种该物质的溶液, +每一种有无限多瓶,第i种的溶液体积为v[i],里面含有w[i]单位的该物质 +研究员每次可以选择一瓶溶液, +将其倒入另外一瓶(假设瓶子的容量无限),即可以看作将两个瓶子内的溶液合并 +此时合并的溶液体积和物质含量都等于之前两个瓶子内的之和。 +特别地,如果瓶子A与B的溶液体积相同,那么A与B合并之后 +该物质的含量会产生化学反应,使得该物质含量增加x单位 +研究员的任务是配制溶液体积恰好等于c的,且尽量浓的溶液(即物质含量尽量多) +研究员想要知道物质含量最多是多少 +对于所有数据,1 <= n, v[i], w[i], x, c <= 1000 + +来自谷歌、亚马逊、微软、蔚来、腾讯、字节跳动、Uber +给出两个长度相同的字符串 str1 和 str2 +请你帮忙判断字符串 str1 能不能在 零次 或 多次 转化 后变成字符串 str2 +每一次转化时,你可以将 str1 中出现的 所有 相同字母变成其他 任何 小写英文字母 +只有在字符串 str1 能够通过上述方式顺利转化为字符串 str2 时才能返回 true 。​​ +测试链接 : https://leetcode.cn/problems/string-transforms-into-another-string/ + +来自Indeed、谷歌、亚马逊、领英、英伟达 +一个坐标可以从 -infinity 延伸到 +infinity 的 无限大的 棋盘上 +你的 骑士 驻扎在坐标为 [0, 0] 的方格里。 +骑士的走法和中国象棋中的马相似,走 “日” 字: +即先向左(或右)走 1 格,再向上(或下)走 2 格 +或先向左(或右)走 2 格,再向上(或下)走 1 格 +每次移动,他都可以像中国象棋中的马一样,选八个方向中的一个前进 +返回 骑士前去征服坐标为 [x, y] 的部落所需的最小移动次数 +本题确保答案是一定存在的。 +测试链接 : https://leetcode.cn/problems/minimum-knight-moves/ + +来自小红书、谷歌、Bloomberg、微软、亚马逊、字节跳动、摩根大通、Uber +你会得到一个字符串 text +你应该把它分成 k 个子字符串 (subtext1, subtext2,…, subtextk) +要求满足: +subtexti 是 非空 字符串 +所有子字符串的连接等于 text +( 即subtext1 + subtext2 + ... + subtextk == text ) +subtexti == subtextk - i + 1 表示所有 i 的有效值( 即 1 <= i <= k ) +返回k可能最大值。 +测试链接 : https://leetcode.cn/problems/longest-chunked-palindrome-decomposition/ + + + + + + + +