diff --git a/MCA算法突击课/第03期/mca_09/Code01_ExpressionCompute.java b/MCA算法突击课/第03期/mca_09/Code01_ExpressionCompute.java index 79628f0..bd94531 100644 --- a/MCA算法突击课/第03期/mca_09/Code01_ExpressionCompute.java +++ b/MCA算法突击课/第03期/mca_09/Code01_ExpressionCompute.java @@ -10,6 +10,7 @@ import java.util.LinkedList; // 本题测试链接 : https://leetcode.cn/problems/basic-calculator-iii/ public class Code01_ExpressionCompute { + // "7 + 3 0 * ( 7 + 2 ) + ...." public static int calculate(String str) { return f(str.toCharArray(), 0)[0]; } diff --git a/MCA算法突击课/第03期/mca_09/Code02_LongestSubstringWithoutRepeatingCharacters.java b/MCA算法突击课/第03期/mca_09/Code02_LongestSubstringWithoutRepeatingCharacters.java index 2d92495..5128aa2 100644 --- a/MCA算法突击课/第03期/mca_09/Code02_LongestSubstringWithoutRepeatingCharacters.java +++ b/MCA算法突击课/第03期/mca_09/Code02_LongestSubstringWithoutRepeatingCharacters.java @@ -1,28 +1,54 @@ package 第03期.mca_09; +import java.util.Arrays; + // 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 // 测试链接 : https://leetcode.cn/problems/longest-substring-without-repeating-characters/ public class Code02_LongestSubstringWithoutRepeatingCharacters { - public static int lengthOfLongestSubstring(String s) { - if (s == null || s.equals("")) { + public static int lengthOfLongestSubstring(String str) { + if (str == null || str.length() == 0) { return 0; } - char[] str = s.toCharArray(); - int[] map = new int[256]; - for (int i = 0; i < 256; i++) { - map[i] = -1; - } - map[str[0]] = 0; - int N = str.length; + // 长度 >= 1 + char[] s = str.toCharArray(); + int n = s.length; + int[] preIndex = new int[256]; + Arrays.fill(preIndex, -1); int ans = 1; - int pre = 1; - for (int i = 1; i < N; i++) { - pre = Math.min(i - map[str[i]], pre + 1); - ans = Math.max(ans, pre); - map[str[i]] = i; + int preAns = 1; + preIndex[s[0]] = 0; + for (int i = 1; i < n; i++) { + int preApear = preIndex[s[i]]; + int p1 = i - preApear; + int p2 = preAns + 1; + int curAns = Math.min(p1, p2); + ans = Math.max(ans, curAns); + preIndex[s[i]] = i; + preAns = curAns; } return ans; } +// public static int lengthOfLongestSubstring(String s) { +// if (s == null || s.equals("")) { +// return 0; +// } +// char[] str = s.toCharArray(); +// int[] map = new int[256]; +// for (int i = 0; i < 256; i++) { +// map[i] = -1; +// } +// map[str[0]] = 0; +// int N = str.length; +// int ans = 1; +// int pre = 1; +// for (int i = 1; i < N; i++) { +// pre = Math.min(i - map[str[i]], pre + 1); +// ans = Math.max(ans, pre); +// map[str[i]] = i; +// } +// return ans; +// } + } diff --git a/MCA算法突击课/第03期/mca_09/Code05_CoinChange.java b/MCA算法突击课/第03期/mca_09/Code05_CoinChange.java deleted file mode 100644 index 5101fc7..0000000 --- a/MCA算法突击课/第03期/mca_09/Code05_CoinChange.java +++ /dev/null @@ -1,91 +0,0 @@ -package 第03期.mca_09; - -// 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额 -// 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 -// 假设每一种面额的硬币有无限个 -// 题目数据保证结果符合 32 位带符号整数 -// 测试链接 : https://leetcode.cn/problems/coin-change-ii/ -public class Code05_CoinChange { - - public static int change1(int aim, int[] arr) { - return process1(arr, 0, aim); - } - - public static int process1(int[] arr, int i, int j) { - if (j < 0) { - return 0; - } - if (i == arr.length) { - return j == 0 ? 1 : 0; - } - return process1(arr, i + 1, j) + process1(arr, i, j - arr[i]); - } - - public static int change2(int aim, int[] arr) { - int n = arr.length; - int[][] dp = new int[n][aim + 1]; - for (int i = 0; i < n; i++) { - for (int j = 0; j <= aim; j++) { - dp[i][j] = -1; - } - } - return process2(arr, 0, aim, dp); - } - - public static int process2(int[] arr, int i, int j, int[][] dp) { - if (j < 0) { - return 0; - } - if (i == arr.length) { - return j == 0 ? 1 : 0; - } - if (dp[i][j] != -1) { - return dp[i][j]; - } - int ans = process2(arr, i + 1, j, dp) + process2(arr, i, j - arr[i], dp); - dp[i][j] = ans; - return ans; - } - - public static int change3(int aim, int[] arr) { - int n = arr.length; - int[][] dp = new int[n + 1][aim + 1]; - dp[n][0] = 1; - for (int i = n - 1; i >= 0; i--) { - for (int j = 0; j <= aim; j++) { - dp[i][j] = dp[i + 1][j]; - if (j - arr[i] >= 0) { - dp[i][j] += dp[i][j - arr[i]]; - } - } - } - return dp[0][aim]; - } - - public static int change4(int aim, int[] arr) { - int n = arr.length; - int[] dp = new int[aim + 1]; - dp[0] = 1; - for (int i = n - 1; i >= 0; i--) { - for (int j = 0; j <= aim; j++) { - if (j - arr[i] >= 0) { - dp[j] += dp[j - arr[i]]; - } - } - } - return dp[aim]; - } - - public static int change5(int aim, int[] arr) { - int n = arr.length; - int[] dp = new int[aim + 1]; - dp[0] = 1; - for (int i = n - 1; i >= 0; i--) { - for (int j = arr[i]; j <= aim; j++) { - dp[j] += dp[j - arr[i]]; - } - } - return dp[aim]; - } - -} \ No newline at end of file diff --git a/MCA算法突击课/第03期/mca_09/Code06_LongestCommonSubsequence.java b/MCA算法突击课/第03期/mca_09/Code06_LongestCommonSubsequence.java deleted file mode 100644 index d30ced1..0000000 --- a/MCA算法突击课/第03期/mca_09/Code06_LongestCommonSubsequence.java +++ /dev/null @@ -1,131 +0,0 @@ -package 第03期.mca_09; - -// 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度 -// 如果不存在 公共子序列 ,返回 0 -// 一个字符串的 子序列 是指这样一个新的字符串: -// 它是由原字符串在不改变字符的相对顺序的情况下 -// 删除某些字符(也可以不删除任何字符)后组成的新字符串 -// 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列 -// 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列 -// 这个问题leetcode上可以直接测 -// 链接:https://leetcode.cn/problems/longest-common-subsequence/ -public class Code06_LongestCommonSubsequence { - - public static int longestCommonSubsequence1(String s1, String s2) { - if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { - return 0; - } - char[] str1 = s1.toCharArray(); - char[] str2 = s2.toCharArray(); - // 尝试 - return process1(str1, str2, str1.length - 1, str2.length - 1); - } - - // str1[0...i]和str2[0...j],这个范围上最长公共子序列长度是多少? - // 可能性分类: - // a) 最长公共子序列,一定不以str1[i]字符结尾、也一定不以str2[j]字符结尾 - // b) 最长公共子序列,可能以str1[i]字符结尾、但是一定不以str2[j]字符结尾 - // c) 最长公共子序列,一定不以str1[i]字符结尾、但是可能以str2[j]字符结尾 - // d) 最长公共子序列,必须以str1[i]字符结尾、也必须以str2[j]字符结尾 - // 注意:a)、b)、c)、d)并不是完全互斥的,他们可能会有重叠的情况 - // 但是可以肯定,答案不会超过这四种可能性的范围 - // 那么我们分别来看一下,这几种可能性怎么调用后续的递归。 - // a) 最长公共子序列,一定不以str1[i]字符结尾、也一定不以str2[j]字符结尾 - // 如果是这种情况,那么有没有str1[i]和str2[j]就根本不重要了,因为这两个字符一定没用啊 - // 所以砍掉这两个字符,最长公共子序列 = str1[0...i-1]与str2[0...j-1]的最长公共子序列长度(后续递归) - // b) 最长公共子序列,可能以str1[i]字符结尾、但是一定不以str2[j]字符结尾 - // 如果是这种情况,那么我们可以确定str2[j]一定没有用,要砍掉;但是str1[i]可能有用,所以要保留 - // 所以,最长公共子序列 = str1[0...i]与str2[0...j-1]的最长公共子序列长度(后续递归) - // c) 最长公共子序列,一定不以str1[i]字符结尾、但是可能以str2[j]字符结尾 - // 跟上面分析过程类似,最长公共子序列 = str1[0...i-1]与str2[0...j]的最长公共子序列长度(后续递归) - // d) 最长公共子序列,必须以str1[i]字符结尾、也必须以str2[j]字符结尾 - // 同时可以看到,可能性d)存在的条件,一定是在str1[i] == str2[j]的情况下,才成立的 - // 所以,最长公共子序列总长度 = str1[0...i-1]与str2[0...j-1]的最长公共子序列长度(后续递归) + 1(共同的结尾) - // 综上,四种情况已经穷尽了所有可能性。四种情况中取最大即可 - // 其中b)、c)一定参与最大值的比较, - // 当str1[i] == str2[j]时,a)一定比d)小,所以d)参与 - // 当str1[i] != str2[j]时,d)压根不存在,所以a)参与 - // 但是再次注意了! - // a)是:str1[0...i-1]与str2[0...j-1]的最长公共子序列长度 - // b)是:str1[0...i]与str2[0...j-1]的最长公共子序列长度 - // c)是:str1[0...i-1]与str2[0...j]的最长公共子序列长度 - // a)中str1的范围 < b)中str1的范围,a)中str2的范围 == b)中str2的范围 - // 所以a)不用求也知道,它比不过b)啊,因为有一个样本的范围比b)小啊! - // a)中str1的范围 == c)中str1的范围,a)中str2的范围 < c)中str2的范围 - // 所以a)不用求也知道,它比不过c)啊,因为有一个样本的范围比c)小啊! - // 至此,可以知道,a)就是个垃圾,有它没它,都不影响最大值的决策 - // 所以,当str1[i] == str2[j]时,b)、c)、d)中选出最大值 - // 当str1[i] != str2[j]时,b)、c)中选出最大值 - public static int process1(char[] str1, char[] str2, int i, int j) { - if (i == 0 && j == 0) { - // str1[0..0]和str2[0..0],都只剩一个字符了 - // 那如果字符相等,公共子序列长度就是1,不相等就是0 - // 这显而易见 - return str1[i] == str2[j] ? 1 : 0; - } else if (i == 0) { - // 这里的情况为: - // str1[0...0]和str2[0...j],str1只剩1个字符了,但是str2不只一个字符 - // 因为str1只剩一个字符了,所以str1[0...0]和str2[0...j]公共子序列最多长度为1 - // 如果str1[0] == str2[j],那么此时相等已经找到了!公共子序列长度就是1,也不可能更大了 - // 如果str1[0] != str2[j],只是此时不相等而已, - // 那么str2[0...j-1]上有没有字符等于str1[0]呢?不知道,所以递归继续找 - if (str1[i] == str2[j]) { - return 1; - } else { - return process1(str1, str2, i, j - 1); - } - } else if (j == 0) { - // 和上面的else if同理 - // str1[0...i]和str2[0...0],str2只剩1个字符了,但是str1不只一个字符 - // 因为str2只剩一个字符了,所以str1[0...i]和str2[0...0]公共子序列最多长度为1 - // 如果str1[i] == str2[0],那么此时相等已经找到了!公共子序列长度就是1,也不可能更大了 - // 如果str1[i] != str2[0],只是此时不相等而已, - // 那么str1[0...i-1]上有没有字符等于str2[0]呢?不知道,所以递归继续找 - if (str1[i] == str2[j]) { - return 1; - } else { - return process1(str1, str2, i - 1, j); - } - } else { // i != 0 && j != 0 - // 这里的情况为: - // str1[0...i]和str2[0...i],str1和str2都不只一个字符 - // 看函数开始之前的注释部分 - // p1就是可能性c) - int p1 = process1(str1, str2, i - 1, j); - // p2就是可能性b) - int p2 = process1(str1, str2, i, j - 1); - // p3就是可能性d),如果可能性d)存在,即str1[i] == str2[j],那么p3就求出来,参与pk - // 如果可能性d)不存在,即str1[i] != str2[j],那么让p3等于0,然后去参与pk,反正不影响 - int p3 = str1[i] == str2[j] ? (1 + process1(str1, str2, i - 1, j - 1)) : 0; - return Math.max(p1, Math.max(p2, p3)); - } - } - - public static int longestCommonSubsequence2(String s1, String s2) { - if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { - return 0; - } - char[] str1 = s1.toCharArray(); - char[] str2 = s2.toCharArray(); - int N = str1.length; - int M = str2.length; - int[][] dp = new int[N][M]; - dp[0][0] = str1[0] == str2[0] ? 1 : 0; - for (int j = 1; j < M; j++) { - dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1]; - } - for (int i = 1; i < N; i++) { - dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0]; - } - for (int i = 1; i < N; i++) { - for (int j = 1; j < M; j++) { - int p1 = dp[i - 1][j]; - int p2 = dp[i][j - 1]; - int p3 = str1[i] == str2[j] ? (1 + dp[i - 1][j - 1]) : 0; - dp[i][j] = Math.max(p1, Math.max(p2, p3)); - } - } - return dp[N - 1][M - 1]; - } - -} diff --git a/MCA算法突击课/第03期/mca_09/Code07_RegularExpressionMatch.java b/MCA算法突击课/第03期/mca_09/Code07_RegularExpressionMatch.java deleted file mode 100644 index 39fa542..0000000 --- a/MCA算法突击课/第03期/mca_09/Code07_RegularExpressionMatch.java +++ /dev/null @@ -1,140 +0,0 @@ -package 第03期.mca_09; - -// 给你一个字符串 s 和一个字符规律 p -// 请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。 -// '.' 匹配任意单个字符 -// '*' 匹配零个或多个前面的那一个元素 -// 所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。 -// 测试链接 : https://leetcode.cn/problems/regular-expression-matching/ -public class Code07_RegularExpressionMatch { - - public static boolean isValid(char[] s, char[] e) { - // s中不能有'.' or '*' - for (int i = 0; i < s.length; i++) { - if (s[i] == '*' || s[i] == '.') { - return false; - } - } - // 开头的e[0]不能是'*',没有相邻的'*' - for (int i = 0; i < e.length; i++) { - if (e[i] == '*' && (i == 0 || e[i - 1] == '*')) { - return false; - } - } - return true; - } - - // 初始尝试版本,不包含斜率优化 - public static boolean isMatch1(String str, String exp) { - if (str == null || exp == null) { - return false; - } - char[] s = str.toCharArray(); - char[] e = exp.toCharArray(); - return isValid(s, e) && process(s, e, 0, 0); - } - - // str[si.....] 能不能被 exp[ei.....]配出来! true false - public static boolean process(char[] s, char[] e, int si, int ei) { - if (ei == e.length) { // exp 没了 str? - return si == s.length; - } - // exp[ei]还有字符 - // ei + 1位置的字符,不是* - if (ei + 1 == e.length || e[ei + 1] != '*') { - // ei + 1 不是* - // str[si] 必须和 exp[ei] 能配上! - return si != s.length && (e[ei] == s[si] || e[ei] == '.') && process(s, e, si + 1, ei + 1); - } - // exp[ei]还有字符 - // ei + 1位置的字符,是*! - while (si != s.length && (e[ei] == s[si] || e[ei] == '.')) { - if (process(s, e, si, ei + 2)) { - return true; - } - si++; - } - return process(s, e, si, ei + 2); - } - - // 改记忆化搜索+斜率优化 - public static boolean isMatch2(String str, String exp) { - if (str == null || exp == null) { - return false; - } - char[] s = str.toCharArray(); - char[] e = exp.toCharArray(); - if (!isValid(s, e)) { - return false; - } - int[][] dp = new int[s.length + 1][e.length + 1]; - // dp[i][j] = 0, 没算过! - // dp[i][j] = -1 算过,返回值是false - // dp[i][j] = 1 算过,返回值是true - return isValid(s, e) && process2(s, e, 0, 0, dp); - } - - public static boolean process2(char[] s, char[] e, int si, int ei, int[][] dp) { - if (dp[si][ei] != 0) { - return dp[si][ei] == 1; - } - boolean ans = false; - if (ei == e.length) { - ans = si == s.length; - } else { - if (ei + 1 == e.length || e[ei + 1] != '*') { - ans = si != s.length && (e[ei] == s[si] || e[ei] == '.') && process2(s, e, si + 1, ei + 1, dp); - } else { - if (si == s.length) { // ei ei+1 * - ans = process2(s, e, si, ei + 2, dp); - } else { // si没结束 - if (s[si] != e[ei] && e[ei] != '.') { - ans = process2(s, e, si, ei + 2, dp); - } else { // s[si] 可以和 e[ei]配上 - ans = process2(s, e, si, ei + 2, dp) || process2(s, e, si + 1, ei, dp); - } - } - } - } - dp[si][ei] = ans ? 1 : -1; - return ans; - } - - // 动态规划版本 + 斜率优化 - public static boolean isMatch3(String str, String pattern) { - if (str == null || pattern == null) { - return false; - } - char[] s = str.toCharArray(); - char[] p = pattern.toCharArray(); - if (!isValid(s, p)) { - return false; - } - int N = s.length; - int M = p.length; - boolean[][] dp = new boolean[N + 1][M + 1]; - dp[N][M] = true; - for (int j = M - 1; j >= 0; j--) { - dp[N][j] = (j + 1 < M && p[j + 1] == '*') && dp[N][j + 2]; - } - // dp[0..N-2][M-1]都等于false,只有dp[N-1][M-1]需要讨论 - if (N > 0 && M > 0) { - dp[N - 1][M - 1] = (s[N - 1] == p[M - 1] || p[M - 1] == '.'); - } - for (int i = N - 1; i >= 0; i--) { - for (int j = M - 2; j >= 0; j--) { - if (p[j + 1] != '*') { - dp[i][j] = ((s[i] == p[j]) || (p[j] == '.')) && dp[i + 1][j + 1]; - } else { - if ((s[i] == p[j] || p[j] == '.') && dp[i + 1][j]) { - dp[i][j] = true; - } else { - dp[i][j] = dp[i][j + 2]; - } - } - } - } - return dp[0][0]; - } - -} diff --git a/MCA算法突击课/第03期/mca_09/Test.java b/MCA算法突击课/第03期/mca_09/Test.java new file mode 100644 index 0000000..78a4a38 --- /dev/null +++ b/MCA算法突击课/第03期/mca_09/Test.java @@ -0,0 +1,154 @@ +package 第03期.mca_09; + +public class Test { + + public static int f(int n) { + if (n == 1) { + return 1; + } + if (n == 2) { + return 1; + } + // n > 2 + return f(n - 1) + f(n - 2); + } + + public static int f2(int n) { + int[] dp = new int[n + 1]; + return p2(n, dp); + } + + public static int p2(int i, int[] dp) { + if (i == 1) { + return 1; + } + if (i == 2) { + return 1; + } + // i > 2 + if (dp[i] != 0) { + return dp[i]; + } + int ans = p2(i - 1, dp) + p2(i - 2, dp); + dp[i] = ans; + return ans; + } + + public static void main(String[] args) { + int n = 7; + // 1 1 2 3 5 8 13 + System.out.println("菲数列第 " + n + " 项,为" + f(n)); + } + + // 彻底暴力 + public static int knapsack1(int[] w, int[] v, int bag) { + // 0...... bag + // 返回最大价值 + return process(w, v, 0, bag); + } + + // i....... 货自由选择,背包的剩余载重是j + // 返回最大价值 + public static int process(int[] w, int[] v, int i, int j) { + if (j < 0) { + // 剩余载重是负数!??? + // 说明之前的选择有错,返回-1表示无效 + return -1; + } + // j >= 0; + if (i == w.length) { + // 没货了! + return 0; + } + // 背包j >= 0 + // 有货! + // 可能性1,不要当前的货 + int p1 = process(w, v, i + 1, j); + // 可能性2,要当前的货 + int p2 = 0; + int next2 = process(w, v, i + 1, j - w[i]); + if (next2 != -1) { + p2 = next2 + v[i]; + } + return Math.max(p1, p2); + } + + // 记忆化搜索 + // 货物数量n <= 1000 + // 背包容量bag <= 1000 + public static int knapsack2(int[] w, int[] v, int bag) { + int n = w.length; + int[][] dp = new int[n][bag + 1]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j <= bag; j++) { + dp[i][j] = -2; + } + } + return process2(w, v, 0, bag, dp); + } + + // i....... 货自由选择,背包的剩余载重是j + // 返回最大价值 + public static int process2(int[] w, int[] v, int i, int j, int[][] dp) { + if (j < 0) { + return -1; + } + if (i == w.length) { + return 0; + } + if (dp[i][j] != -2) { + return dp[i][j]; + } + int p1 = process2(w, v, i + 1, j, dp); + int p2 = 0; + int next2 = process2(w, v, i + 1, j - w[i], dp); + if (next2 != -1) { + p2 = next2 + v[i]; + } + int ans = Math.max(p1, p2); + dp[i][j] = ans; + return ans; + } + + public static int knapsack3(int[] w, int[] v, int bag) { + int n = w.length; + int[][] dp = new int[n + 1][bag + 1]; + // dp[n][...] = 0 + for (int i = n - 1; i >= 0; i--) { + // 每个格子依赖下一行的,两个位置的值 + for (int j = 0; j <= bag; j++) { + // dp[i][j] + int p1 = dp[i + 1][j]; + int p2 = 0; + if (j - w[i] >= 0) { + p2 = v[i] + dp[i + 1][j - w[i]]; + } + dp[i][j] = Math.max(p1, p2); + } + } + return dp[0][bag]; + } + + public static int knapsack4(int[] w, int[] v, int bag) { + int n = w.length; + int[] dp = new int[bag + 1]; + // 脑中[n][...] = 0 + for (int i = n - 1; i >= 0; i--) { + // 每个格子依赖下一行的,两个位置的值 + for (int j = bag; j >= 0; j--) { + // 0 1 2 3 4 5 6 + // <- + int p1 = dp[j]; + int p2 = 0; + if (j - w[i] >= 0) { + p2 = v[i] + dp[j - w[i]]; + } + dp[j] = Math.max(p1, p2); + } + } + // 第0行的值 + return dp[bag]; + } + +} diff --git a/MCA算法突击课/第03期/ppt/key/第09节.key b/MCA算法突击课/第03期/ppt/key/第09节.key new file mode 100644 index 0000000..447d44c Binary files /dev/null and b/MCA算法突击课/第03期/ppt/key/第09节.key differ diff --git a/MCA算法突击课/第03期/ppt/powerpoint/第09节.pptx b/MCA算法突击课/第03期/ppt/powerpoint/第09节.pptx new file mode 100644 index 0000000..09cdb75 Binary files /dev/null and b/MCA算法突击课/第03期/ppt/powerpoint/第09节.pptx differ