diff --git a/src/main/java/dynamic_programming/IsSubsequence.java b/src/main/java/dynamic_programming/IsSubsequence.java new file mode 100644 index 0000000..e5b8d66 --- /dev/null +++ b/src/main/java/dynamic_programming/IsSubsequence.java @@ -0,0 +1,57 @@ +package dynamic_programming; +/** + * 题目: 392. 判断子序列 (isSubsequence) + * 描述:给定字符串 s 和 t ,判断 s 是否为 t 的子序列。 + * 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。 + * + * 进阶: + * 如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码? + + 示例 1: + 输入:s = "abc", t = "ahbgdc" + 输出:true + + * 链接:https://leetcode.cn/problems/is-subsequence/ + */ +//不会 +public class IsSubsequence { + //动态规划:转为求最长公共子序列,是否为s的长度 + public boolean isSubsequence(String s, String t) { + int length1 = s.length(); int length2 = t.length(); + int[][] dp = new int[length1+1][length2+1]; + for(int i = 1; i <= length1; i++){ + for(int j = 1; j <= length2; 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]; //s长度肯定小于等于t 要删也删t + } + } + } + return dp[length1][length2] == length1; + } + //双指针 + + /** + * 我们用两个指针 i 遍历字符串 s,j 遍历字符串 t。初始均指向各自字符串的开头: + * + * 如果 s.charAt(i) == t.charAt(j),说明匹配上了,两个指针都往后走:i++, j++。 + * 否则,就只能在 t 上“跳过”这个字符,j++。 + * 当 i 走到 s.length() 时,说明 s 中的所有字符都在 t 中按顺序找到了,返回 true;否则,等 j 扫完 t 还没把 s 的所有字符匹配完,就返回 false。 + */ + public boolean isSubsequence2(String s, String t) { + int i = 0, j = 0; + int n = s.length(), m = t.length(); + // 当 i < n 且 j < m 时循环 + while (i < n && j < m) { + if (s.charAt(i) == t.charAt(j)) { + // 匹配时,两指针都前进一步 + i++; + } + // 不匹配时,只在 t 上跳过 + j++; + } + // 如果 i 已经走完 s,说明全部匹配 + return i == n; + } +} diff --git a/src/main/java/dynamic_programming/MinPathSum.java b/src/main/java/dynamic_programming/MinPathSum.java new file mode 100644 index 0000000..050adf5 --- /dev/null +++ b/src/main/java/dynamic_programming/MinPathSum.java @@ -0,0 +1,33 @@ +package dynamic_programming; +/** + * 题目: 64. 最小路径和 (MinPathSum) + * 描述:给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + * 说明:每次只能向下或者向右移动一步。 + + 示例 1: + 输入:grid = [[1,3,1],[1,5,1],[4,2,1]] + 输出:7 + 解释:因为路径 1→3→1→1→1 的总和最小。 + + * 链接:https://leetcode.cn/problems/minimum-path-sum/ + */ +public class MinPathSum { + public int minPathSum(int[][] grid) { + int r=grid.length; + int c=grid[0].length; + int[][]dp=new int[r][c]; + dp[0][0]=grid[0][0]; + for (int i = 1; i < r; i++) { + dp[i][0]=dp[i-1][0]+grid[i][0]; + } + for (int i = 1; i < c; i++) { + dp[0][i]=dp[0][i-1]+grid[0][i]; + } + for (int i = 1; i < r; i++) { + for (int j = 1; j < c; j++) { + dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j]; + } + } + return dp[r-1][c-1]; + } +} diff --git a/src/main/java/dynamic_programming/NumDistinct.java b/src/main/java/dynamic_programming/NumDistinct.java new file mode 100644 index 0000000..a3b31bd --- /dev/null +++ b/src/main/java/dynamic_programming/NumDistinct.java @@ -0,0 +1,44 @@ +package dynamic_programming; +/** + * 题目: 115. 不同的子序列 (numDistinct) + * 描述:给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数。 + * + * 测试用例保证结果在 32 位有符号整数范围内。 + + 示例 1: + 输入:s = "rabbbit", t = "rabbit" + 输出:3 + 解释: + 如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。 + rabbbit + rabbbit + rabbbit + + * 链接:https://leetcode.cn/problems/distinct-subsequences/ + */ +//不会 +public class NumDistinct { + public int numDistinct(String s, String t) { + int n = s.length(), m = t.length(); + // dp[i][j]: s[0..i-1] 中 t[0..j-1] 的方案数 + long[][] dp = new long[n+1][m+1]; + // 初始化 + for (int i = 0; i <= n; i++) { + dp[i][0] = 1; + } + // dp[0][j>0] 默认就是 0 + + // 状态转移 + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (s.charAt(i-1) == t.charAt(j-1)) { + dp[i][j] = dp[i-1][j-1] + dp[i-1][j]; + } else { + dp[i][j] = dp[i-1][j]; + } + } + } + // 由于题目保证答案在 32 位范围内,最后转为 int 返回 + return (int) dp[n][m]; + } +}