97. 交错字符串
leetcode题目链接:https://leetcode.cn/problems/interleaving-string/
leetcode AC记录:
思路:话不多说,动态规划。使用dp[i][j]表示s1从下标0开始,长度为i的子串,和s2从下标0开始,长度为j的子串,能否构成下标从0开始,长度为i+j的子串。状态转移方程为dp[i][j] = (dp[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1)) || (dp[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1))。
代码如下:
public boolean isInterleave(String s1, String s2, String s3) {
boolean[][] dp = new boolean[s1.length()+1][s2.length()+1];
if(s2.length() + s1.length() != s3.length()) {
return false;
}
//dp[i][j]表示s1从下标0开始,长度为i的子串,和s2从下标0开始,长度为j的子串,能否构成下标从0开始,长度为i+j的子串
//状态转移方程为dp[i][j] = (dp[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1)) || (dp[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1))
dp[0][0] = true;
for(int i = 0;i <= s1.length();i++) {
for(int j = 0;j <= s2.length();j++) {
if(i > 0 && j > 0) {
dp[i][j] = (dp[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1)) || (dp[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1));
} else if(i > 0) {
dp[i][j] = dp[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1);
} else if(j > 0) {
dp[i][j] = dp[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1);
}
}
}
return dp[s1.length()][s2.length()];
}
221. 最大正方形
leetcode题目链接:https://leetcode.cn/problems/maximal-square/
leetcode AC记录:
思路:最大正方形,使用dp[i][j]表示以i,j为正方形右下角顶点的最大边长。状态转移方程为dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])。初始化边缘,即i=0,j=[0-length]和i=[0-length][0]为如果[i][j]为1,则面积为1,否则为0。中间使用结果记录最大边长,最后结果是边长的乘积。
代码如下:
public int maximalSquare(char[][] matrix) {
//使用dp[i][j]表示以i,j为正方形右下角的最大正方形面积
//dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1
int[][] dp = new int[matrix.length][matrix[0].length];
int res = 0;
//初始化dp数组
for(int i = 0;i < matrix.length;i++) {
if(matrix[i][0] == '1') {
dp[i][0] = 1;
res = 1;
}
}
for(int i = 0;i < matrix[0].length;i++) {
if(matrix[0][i] == '1') {
dp[0][i] = 1;
res = 1;
}
}
for(int i = 1;i < matrix.length;i++) {
for(int j = 1; j < matrix[0].length;j++) {
if(matrix[i][j] == '1') {
dp[i][j] = Math.min(dp[i-1][j-1], Math.min(dp[i][j-1], dp[i-1][j])) + 1;
}
if(res < dp[i][j]) {
res = dp[i][j];
}
}
}
return res * res;
}
509. 斐波那契数
leetcode题目链接:https://leetcode.cn/problems/fibonacci-number/
leetcode AC记录:
代码如下:
public int fib(int n) {
if(n <= 1) {
return n;
}
int a = 0;
int b = 1;
int c = 0;
for(int i = 2;i <= n;i++) {
c = a + b;
a = b;
b = c;
}
return c;
}
70. 爬楼梯
leetcode题目链接:https://leetcode.cn/problems/climbing-stairs
leetcode AC记录:
代码如下:
public int climbStairs(int n) {
if(n <= 1) {
return n;
}
int a = 1;
int b = 1;
int c = 0;
for(int i = 2;i <= n;i++) {
c = a+b;
a = b;
b = c;
}
return c;
}
746. 使用最小花费爬楼梯
leetcode题目链接:https://leetcode.cn/problems/min-cost-climbing-stairs
leetcode AC记录:
思路:使用dp[i]表示到下标为i所付出的成本,状态转移方程:dp[i] = min(dp[i-1],dp[i-2]) + cost[i]。
代码如下:
public int minCostClimbingStairs(int[] cost) {
int[] dp = new int[cost.length];
//使用dp[i]表示到下标为i所付出的成本
//状态转移方程:dp[i] = min(dp[i-1],dp[i-2]) + cost[i]
dp[0] = cost[0];
dp[1] = cost[1];
for(int i = 2;i < cost.length;i++) {
dp[i] = Math.min(dp[i-1], dp[i-2]) + cost[i];
}
return Math.min(dp[cost.length-1], dp[cost.length-2]);
}
1137. 第 N 个泰波那契数
leetcode题目链接:https://leetcode.cn/problems/n-th-tribonacci-number/
leetcode AC记录:
代码如下:
public int tribonacci(int n) {
if(n == 0) {
return 0;
} else if(n <= 2) {
return 1;
}
int a = 0;
int b = 1;
int c = 1;
int d = 0;
for(int i = 3;i <= n;i++) {
d = a + b + c;
a = b;
b = c;
c = d;
}
return d;
}
873. 最长的斐波那契子序列的长度
leetcode题目链接:https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence
leetcode AC记录:
思路:使用dp[j][i]表示以j和i下标所在位置为末尾的斐波纳切数列的最大长度。状态转移方程为:dp[j][i] =
Math.max(dp[index][j] + 1, 3),index < j && index >= 0
0, index = -1
其中使用map记录arr[i]和i的关系,index = map.getOrDefault(arr[i]-arr[j], -1)
代码如下:
public int lenLongestFibSubseq(int[] arr) {
//使用dp[i][j]表示数组arr以下标i作为结尾的斐波那契数列最大长度,j为最长菲波纳切数列的倒数第二个元素下标
Map<Integer, Integer> map = new HashMap<>(arr.length);
for(int i = 0; i < arr.length;i++) {
map.put(arr[i], i);
}
int[][] dp = new int[arr.length][arr.length];
int res = 0;
for(int i = 2;i < arr.length;i++) {
for(int j = i-1; j >= 0;j--) {
int diff = arr[i] - arr[j];
int index = map.getOrDefault(diff, -1);
if(index >= 0 && index < j) {
dp[j][i] = Math.max(dp[index][j] + 1, 3);
}
if(res < dp[j][i]) {
res = dp[j][i];
}
}
}
return res;
}
62. 不同路径
leetcode题目链接:https://leetcode.cn/problems/unique-paths
leetcode AC记录:
代码如下:
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for(int i = 0;i < m;i++) {
dp[i][0] = 1;
}
for(int i = 0;i < n;i++) {
dp[0][i] = 1;
}
for(int i = 1;i < m;i++) {
for(int j = 1;j < n;j++) {
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
63.不同路径II
leetcode题目链接:https://leetcode.cn/problems/unique-paths-ii/
leetcode AC记录:
思路:如果obstacleGrid[0][0]等于1的话,直接返回0。使用动态规划,dp[i][j]保存的是从 0,0 到当前位置的路径总和,初始化操作:
初始化dp数组第一列和第一行,如果当前数字为1,dp数组的值为0;如果当前数字为0的话,dp数组的值为1;
遍历obstacleGrid数组,如果当前位置的值是1的话,路径和为0;如果当前位置的值是0的话,路径和的状态转移方程为dp[i][j]=dp[i-1][j-1]。
代码如下:
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int[][] dp = new int[obstacleGrid.length][obstacleGrid[0].length];
if(obstacleGrid[0][0] == 1) {
return 0;
}
dp[0][0] = 1;
for(int i = 1;i < obstacleGrid.length;i++) {
if(obstacleGrid[i][0] == 0) {
dp[i][0] = dp[i-1][0] == 0 ? 0 : 1;
}
}
for(int i = 1;i < obstacleGrid[0].length;i++) {
if(obstacleGrid[0][i] == 0) {
dp[0][i] = dp[0][i-1] == 0 ? 0 : 1;
}
}
for(int i = 1;i < obstacleGrid.length;i++) {
for(int j = 1;j < obstacleGrid[0].length;j++) {
if(obstacleGrid[i][j] == 0) {
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
}
return dp[obstacleGrid.length-1][obstacleGrid[0].length-1];
}
343. 整数拆分
leetcode题目链接:https://leetcode.cn/problems/integer-break
leetcode AC记录:
思路:动态规划,使用dp[i]存储整数和为i的最大乘积。状态转移方程为dp[i] = max(dp[j] * (i-j), dp[i], (i-j)* j)。
代码如下:
public int integerBreak(int n) {
//dp【i】数组表示以i为和的最大乘积
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
dp[2] = 1;
for(int i = 3;i <= n;i++) {
for(int j = i-1;j >= 1;j--) {
dp[i] = Math.max(Math.max(dp[j] * (i-j), dp[i]), j * (i-j));
}
}
return dp[n];
}
96. 不同的二叉搜索树
leetcode题目链接:https://leetcode.cn/problems/unique-binary-search-trees
leetcode AC记录:
思路:使用动态规划。dp[i]表示以节点1到i组成的二叉搜索树的总量,状态转移方程为dp[i] = sum(dp[k] * dp[i-k-1])。因为都是顺序递增的,所以说只用关心节点的数量就行,不用关心节点具体的值。举个例子,比如以1到3组成的二叉搜索树的总量计算过程为:分别以1,2,3为头节点,
以1为头节点时,将数组分为左右子树【左子树:null】【头节点1】【右子树:2,3】,结果为dp[0] * dp[2];
以2为头节点时,将数组分为左右子树【左子树:1】【头节点2】【右子树:3】,结果为dp[1] * dp[1];
以3为头节点时,将数组分为左右子树【左子树:1,2】【头节点3】【右子树:null】,结果为dp[2] * dp[0];
所以dp[3] = dp[0] * dp[2] + dp[1] * dp[1] + dp[2] * dp[0],符合我们的状态转移方程。
其中,初始化操作为:dp[0] = 1, dp[1] = 1, dp[2] = 2。
代码如下:
public int numTrees(int n) {
//使用dp【i】表示以节点1到i的二叉搜索树的总量
//状态转移方程为dp[i] = sum(dp[k] * dp[i-k-1])
int[] dp = new int[n+1];
if(n <= 2) {
return n == 2 ? 2 : 1;
}
dp[0] =1;
dp[1] = 1;
dp[2] = 2;
for(int i = 3;i <= n;i++) {
for(int k = 0;k < i;k++) {
dp[i] += dp[k] * dp[i-k-1];
}
}
return dp[n];
}
279.完全平方数
leetcode题目链接:https://leetcode.cn/problems/perfect-squares/
leetcode AC记录:
思路:动态规划,使用dp[i]表示组成和为i的完全平方数的最小个数。状态转移方程为dp[i] = dp[i-j*j] +1。
代码如下:
public int numSquares(int n) {
//使用动态规划
//状态转移方程为dp[i] = dp[j] + dp[i-(j*j)]
int[] dp = new int[n+1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
dp[1] = 1;
for(int i = 2;i <= n;i++) {
for(int j = 1;j * j <= n;j++) {
if(i - j*j >= 0) {
dp[i] = Math.min(dp[i-j*j]+1, dp[i]);
}
}
}
return dp[n];
}