class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
// 获取三角形的行数
int n = triangle.size();
// 创建二维数组 f,用于存储从顶部到底部不同路径的最小路径和
int[][] f = new int[n][n];
// 初始化第一行的最小路径和,即第一个元素本身
f[0][0] = triangle.get(0).get(0);
// 遍历每一行(从第二行开始)
for (int i = 1; i < n; ++i) {
// 处理每行的第一个元素,其最小路径和为上一行第一个元素的路径和加上当前行第一个元素的值
f[i][0] = f[i - 1][0] + triangle.get(i).get(0);
// 遍历当前行中间的元素
for (int j = 1; j < i; ++j) {
// 当前位置的最小路径和为上一行相邻两个位置(j-1 和 j)的最小路径和中的较小值,加上当前位置的元素值
f[i][j] = Math.min(f[i - 1][j - 1], f[i - 1][j]) + triangle.get(i).get(j);
}
// 处理每行的最后一个元素,其最小路径和为上一行最后一个元素的路径和加上当前行最后一个元素的值
f[i][i] = f[i - 1][i - 1] + triangle.get(i).get(i);
}
// 初始化最小路径和为最后一行的第一个元素
int minTotal = f[n - 1][0];
// 遍历最后一行的所有元素,找到最小路径和
for (int i = 1; i < n; ++i) {
minTotal = Math.min(minTotal, f[n - 1][i]);
}
// 返回最小路径和
return minTotal;
}
}
class Solution {
public int minPathSum(int[][] grid) {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (i == 0 && j == 0) {
continue;
}
if (i == 0) {
grid[i][j] = grid[i][j - 1] + grid[i][j];
} else if (j == 0) {
grid[i][j] = grid[i - 1][j] + grid[i][j];
} else {
grid[i][j] = Math.min(grid[i][j - 1], grid[i - 1][j]) + grid[i][j];
}
}
}
return grid[grid.length - 1][grid[0].length - 1];
}
}
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
// 获取二维数组的行数,代表网格的高度
int n = obstacleGrid.length;
// 获取二维数组的列数,代表网格的宽度
int m = obstacleGrid[0].length;
// 创建一个长度为列数的一维数组 f,用于存储到达每个位置的不同路径数量
int[] f = new int[m];
// 初始化第一个位置的路径数量
// 如果起始位置没有障碍物,那么起始位置的路径数量为 1,否则为 0
f[0] = obstacleGrid[0][0] == 0? 1 : 0;
// 遍历每一行
for (int i = 0; i < n; ++i) {
// 遍历每一列
for (int j = 0; j < m; ++j) {
// 如果当前位置有障碍物
if (obstacleGrid[i][j] == 1) {
// 将当前位置的路径数量设置为 0,因为有障碍物无法通过
f[j] = 0;
// 继续下一个位置的判断
continue;
}
// 如果当前位置不是第一列且左边位置没有障碍物
if (j - 1 >= 0 && obstacleGrid[i][j - 1] == 0) {
// 当前位置的路径数量等于当前位置原本的路径数量加上左边位置的路径数量
// 因为可以从左边位置到达当前位置
f[j] += f[j - 1];
}
}
}
// 返回最后一个位置的路径数量,即从左上角到右下角的不同路径数量
return f[m - 1];
}
}
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
// 如果字符串长度小于 2,直接返回该字符串,因为单个字符或空字符串都是回文串
if (n < 2) {
return s;
}
// 创建一个二维布尔数组 dp,dp[i][j] 表示字符串 s 从索引 i 到索引 j 的子串是否是回文串
boolean[][] dp = new boolean[n][n];
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < n; i++) {
dp[i][i] = true;
}
int maxLen = 1;
int begin = 0;
char[] charArray = s.toCharArray();
// 遍历不同长度的子串
for (int L = 2; L <= n; L++) {
// 遍历字符串的起始位置
for (int i = 0; i < n; i++) {
int j = L + i - 1;
// 如果右边界超出字符串范围,退出当前循环
if (j >= n) {
break;
}
if (charArray[i]!= charArray[j]) {
// 如果首尾字符不相等,该子串不是回文串
dp[i][j] = false;
} else {
// 如果首尾字符相等,分情况判断
if (L < 3) {
// 当子串长度小于 3 时,只要首尾字符相等就是回文串
dp[i][j] = true;
} else {
// 当子串长度大于等于 3 时,取决于去掉首尾字符后的子串是否是回文串
dp[i][j] = dp[i + 1][j - 1];
}
}
// 如果当前子串是回文串且长度大于已记录的最大长度,更新最大长度和起始位置
if (dp[i][j] && L > maxLen) {
maxLen = Math.max(maxLen, L);
begin = i;
}
}
}
// 根据记录的起始位置和最大长度,从原始字符串中截取最长回文子串并返回
return s.substring(begin, begin + maxLen);
}
}
class Solution {
public boolean isInterleave(String s1, String s2, String s3) {
int m = s1.length();
int n = s2.length();
int t = s3.length();
if (m + n != t) {
return false;
}
boolean[][] f = new boolean[m + 1][n + 1];
f[0][0] = true;
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
int p = i + j - 1;
if (i > 0) {
f[i][j] = (f[i][j]) || (f[i - 1][j] && s1.charAt(i - 1) == s3.charAt(p));
}
if (j > 0) {
f[i][j] = (f[i][j]) || (f[i][j - 1] && s2.charAt(j - 1) == s3.charAt(p));
}
}
}
return f[m][n];
}
}
class Solution {
public int minDistance(String word1, String word2) {
int n = word1.length();
int m = word2.length();
// 有一个字符串为空串
if (n * m == 0) {
return n + m;
}
// DP 数组,D[i][j] 表示 word1 的前 i 个字符转换为 word2 的前 j 个字符所需的最少操作次数
int[][] D = new int[n + 1][m + 1];
// 边界状态初始化
// 当 word2 为空串时,word1 的每个字符都需要删除,所以 D[i][0] = i
for (int i = 0; i < n + 1; i++) {
D[i][0] = i;
}
// 当 word1 为空串时,word2 的每个字符都需要插入,所以 D[0][j] = j
for (int j = 0; j < m + 1; j++) {
D[0][j] = j;
}
// 计算所有 DP 值
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < m + 1; j++) {
int left = D[i - 1][j] + 1;
// left 表示对 word1 的前 i-1 个字符转换为 word2 的前 j 个字符后,再删除 word1 的第 i 个字符的操作次数
int down = D[i][j - 1] + 1;
// down 表示对 word1 的前 i 个字符转换为 word2 的前 j-1 个字符后,再插入 word2 的第 j 个字符的操作次数
int left_down = D[i - 1][j - 1];
// left_down 表示 word1 的前 i-1 个字符转换为 word2 的前 j-1 个字符的操作次数
if (word1.charAt(i - 1)!= word2.charAt(j - 1)) {
// 如果 word1 的第 i 个字符和 word2 的第 j 个字符不同,需要进行替换操作,操作次数加一
left_down += 1;
}
// 取三种操作中的最小值作为 word1 的前 i 个字符转换为 word2 的前 j 个字符的最少操作次数
D[i][j] = Math.min(left, Math.min(down, left_down));
}
}
return D[n][m];
}
}
class Solution {
public int maximalSquare(char[][] matrix) {
int rows = matrix.length;
int columns = matrix[0].length;
int maxSide = 0;
if (matrix == null || rows == 0 || columns == 0) {
return maxSide;
}
int[][] dp = new int[rows][columns];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (matrix[i][j] == '1') {
if (i == 0 || j == 0) {
dp[i][j] = 1;
} else {
dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
maxSide = Math.max(maxSide, dp[i][j]);
}
}
}
return maxSide * maxSide;
}
}