Leetcode 151 最大正方形 | 买卖股票的最佳时机 III

1 题目

221. 最大正方形

在一个由 '0''1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

示例 1:

复制代码
输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4

示例 2:

复制代码
输入:matrix = [["0","1"],["1","0"]]
输出:1

示例 3:

复制代码
输入:matrix = [["0"]]
输出:0

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 300
  • matrix[i][j]'0''1'

2 代码实现

c++

cpp 复制代码
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if (matrix.empty() || matrix[0].empty()){
            return 0 ; 
        }

        int m = matrix.size(); //行
        int n = matrix[0].size() ; //列

        vector<vector<int>> dp(m , vector<int>(n , 0));
        int maxSide = 0;

        for (int j = 0 ; j < n ; j ++){
            if (matrix[0][j] == '1'){
                dp[0][j] = 1 ;
                maxSide = 1 ;
            }
        }

        for (int i = 1 ; i < m ; i++){
            if (matrix[i][0] == '1'){
                dp[i][0] = 1 ;
                maxSide = 1 ;
            }
        }

        for (int i = 1 ; i< m ; i++){
            for (int j = 1 ; j < n ; j ++){
                if (matrix[i][j] == '1'){
                    dp[i][j] = min (min(dp[i -1][j] , dp[i][j -1 ]) , dp[i -1][ j -1]) + 1 ;
                    maxSide = max(maxSide , dp[i][j]);
                }
            }
        }
        return maxSide * maxSide ; 

    }
};

js

javascript 复制代码
/**
 * @param {character[][]} matrix
 * @return {number}
 */
var maximalSquare = function(matrix) {
    if(matrix.length == 0 || matrix[0].length == 0 ){
        return 0 ;   
    }

    let m = matrix.length ;
    let n = matrix[0].length ;

    const dp = new Array(m);
    for (let i = 0 ; i < m ; i++){
        dp[i] = new Array(n).fill(0);
    }

    let maxSize = 0 ;

    for (let i = 0 ; i < m ; i++){
        if (matrix[i][0] === '1'){
            dp[i][0] = 1 ;
            maxSize = 1 ;
        }
    }

    for (let j = 0 ; j < n ; j++){
        if (matrix[0][j] === '1'){
            dp[0][j] = 1;
            maxSize = 1 ;
        }
    }

    for (let i = 1 ; i < m ; i ++){
        for (let j = 1 ; j < n ; j ++){
            if (matrix[i][j] == '1'){
                dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                maxSize = Math.max (maxSize ,dp[i][j]); 
            }
        }
    }

    return maxSize * maxSize ;

};

思考

真是无语做了这么多动态规划,这个题目一出来我还是没看懂。

正方形啥意思,也就是dp数组如果是维护二维的,那么两个维度的长度要一样长,可是应该怎么走呢,不像之前的有两个选择,我只是知道动规的思想在于用小的推出大的,也就是i - 1 的情况可以推出 i 的情况。

这该怎么办?

题解

我完全懂你的困惑!刚看到这道题时,就算做过很多动态规划,也会懵:二维矩阵、找最大全 1 正方形,完全不知道 DP 状态怎么定义,更不知道怎么转移。

我带你一步步从 0 到 1 理清思路,再写标准 C++ 代码。


一、核心思考(突破点)

1. 先想:暴力解法为什么不行?
  • 遍历每个点作为正方形左上角
  • 尝试扩展边长,检查是否全为 1
  • 时间复杂度 O(mn·min(m,n)²),数据范围 300×300,会超时→ 必须用动态规划优化
2. 动态规划状态定义(关键突破)

我们定义:dp[i][j]:以矩阵中坐标 (i,j) 右下角的最大全 1 正方形的边长

为什么选右下角 ?因为正方形是由上方、左方、左上方三个小正方形拼接而成的,右下角能统一这三个方向的状态。

3. 状态转移方程(核心逻辑)
  1. 如果 matrix[i][j] == '0':这个点不可能作为正方形右下角,所以 dp[i][j] = 0
  2. 如果 matrix[i][j] == '1':能构成的最大正方形边长 = 上方、左方、左上方三个 dp 值的最小值 + 1

公式:dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1

直观理解:三个方向的最小边长决定了当前能扩展的最大上限,+1 就是加上当前这个点。

4. 初始化
  • 第一行、第一列:如果是 '1',dp[i][j] = 1(只能构成边长为 1 的正方形)
  • 用一个变量maxSide记录遍历过程中最大的边长
5. 结果

面积 = 最大边长 × 最大边长


二、C++ 完整代码实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        // 空矩阵直接返回0
        if (matrix.empty() || matrix[0].empty()) return 0;
        
        int m = matrix.size();    // 行数
        int n = matrix[0].size(); // 列数
        // dp数组和原矩阵大小一致
        vector<vector<int>> dp(m, vector<int>(n, 0));
        int maxSide = 0; // 记录最大正方形边长
        
        // 1. 初始化第一行
        for (int j = 0; j < n; j++) {
            if (matrix[0][j] == '1') {
                dp[0][j] = 1;
                maxSide = 1;
            }
        }
        // 2. 初始化第一列(跳过(0,0),避免重复)
        for (int i = 1; i < m; i++) {
            if (matrix[i][0] == '1') {
                dp[i][0] = 1;
                maxSide = 1;
            }
        }
        
        // 3. 遍历剩余位置,状态转移
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == '1') {
                    // 核心转移方程
                    dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                    // 更新最大边长
                    maxSide = max(maxSide, dp[i][j]);
                }
                // matrix[i][j] == '0' 时 dp[i][j] 保持0,无需处理
            }
        }
        
        // 面积 = 边长²
        return maxSide * maxSide;
    }
};

// 测试代码
int main() {
    vector<vector<char>> matrix = {
        {'1','0','1','0','0'},
        {'1','0','1','1','1'},
        {'1','1','1','1','1'},
        {'1','0','0','1','0'}
    };
    Solution sol;
    cout << sol.maximalSquare(matrix) << endl; // 输出4
    return 0;
}

三、代码细节解释

  1. 边界处理:先处理空矩阵,避免数组越界
  2. 初始化 :第一行/列只有1能构成长度为 1 的正方形
  3. 双重循环 :从(1,1)开始遍历,用状态方程计算每个位置的最大边长
  4. 最大值更新:遍历中实时记录最大边长,最后计算面积

四、复杂度分析

  • 时间复杂度:O(mn),只需要遍历一次矩阵
  • 空间复杂度:O(mn),使用了和矩阵等大的 dp 数组✨ 优化:可以用一维 dp 数组将空间降到 O(n),但二维写法更易理解

五、你的困惑总结

你之前卡壳的点:

  1. 不知道 DP 数组定义什么 → 以当前点为右下角的最大正方形边长
  2. 不知道怎么转移 → 取三个方向最小值 + 1
  3. 没想到正方形的拼接规律 → 右下角是最优状态定义点

这道题是二维动态规划的经典模板题,记住这个状态定义和转移方程,同类题直接套用即可!

总结

  1. 状态dp[i][j] = 以(i,j)为右下角的最大全 1 正方形边长
  2. 转移matrix[i][j]='1'时,dp[i][j] = min(上,左,左上)+1
  3. 结果:最大边长的平方就是答案

先记住一句话

dp [i][j] 表示:以 (i,j) 这个格子为右下角,能画出的最大正方形的边长。


一、先看前提

cpp 复制代码
if (matrix[i][j] == '1')

意思很简单:只有当前格子是 1,才有可能构成正方形; 如果是 0,直接跳过,dp 值就是 0。


二、核心代码到底在干嘛?

cpp 复制代码
dp[i][j] = min( 上, 左, 左上 ) + 1;

我给你翻译成人话:

这三个位置分别代表什么?
  • dp [i-1][j] 正上方 → 以正上方为右下角的最大正方形边长
  • dp [i][j-1] 正左方 → 以正左方为右下角的最大正方形边长
  • dp [i-1][j-1] 左上角 → 以左上角为右下角的最大正方形边长
为什么要取 min(最小值)?

因为正方形必须四边相等 。你能拼成多大,取决于最短的那一块

为什么 +1?

因为当前这个 1 自己就是新的一层边长


三、我给你举个超级直观的例子

情况 1
复制代码
上方 = 1
左方 = 1
左上 = 1

最小值 = 1→ 当前 = 1 + 1 = 2

意思:上面、左边、左上角都能构成边长为 1 的正方形, 那我现在这个位置就能构成边长为 2 的正方形!

情况 2
复制代码
上方 = 2
左方 = 2
左上 = 2

最小值 = 2→ 当前 = 2 + 1 = 3

意思:三个方向都能构成边长 2,那我现在能构成边长 3!

情况 3
复制代码
上方 = 2
左方 = 1
左上 = 2

最小值 = 1→ 当前 = 1 + 1 = 2

因为左边最短,只能到 1,所以你最多只能扩展到 2。


四、用一张图彻底看懂

假设现在看的是中间这个格子 (i,j)

复制代码
左上(i-1,j-1)   上方(i-1,j)
                ↑
左方(i,j-1) → 当前(i,j)

要形成正方形,必须三个方向都能补齐短板决定长度


五、再简化到极致

你只需要记住这句口诀:

当前是 1,能拼多大看 上、左、左上 三个里面最小的那个再加 1 就是答案


六、那一句 maxSide 又是干嘛?

cpp 复制代码
maxSide = max(maxSide, dp[i][j]);

很简单:遍历过程中,把所有位置能构成的最大边长记录下来。

最后面积 = 最大边长 × 最大边长。


最终总结(你一定能记住)

  1. 当前格子是 1 才能继续算
  2. 上、左、左上三个值
  3. 最小的那个
  4. +1 就是当前能构成的正方形边长
  5. 全程记录最大边长

自己写js简直是乱写

javascript 复制代码
/**
 * @param {character[][]} matrix
 * @return {number}
 */
var maximalSquare = function(matrix) {
    if(matrix.length == 0 || matrix[0].length == 0 ){
        return 0 ;   
    }

    let m = matrix.length ;
    let n = matrix[0].length ;

    const dp = new Array(m);
    for (let i = 0 ; i < n ; i ++){
        dp[i] = matrix[i];
    }

    let maxSize = 0 ;

    for (let i = 0 ; i < m ; i++){
        if (matrix[i][0] === '1'){
            dp[i][0] = 1 ;
            maxSize = 1 ;
        }
    }

    for (let j = 0 ; j < n ; j++){
        if (matrix[0][j] === '1'){
            dp[0][j] = 1;
            maxSize = 1 ;
        }
    }

    for (let i = 1 ; i < m ; i ++){
        for (let j = 1 ; j < n ; j ++){
            if (matrix[i][j] == '1'){
                dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
                maxSize = Math.max (maxSize ,dp[i][j]); 
            }
        }
    }

    return maxSize * maxSize ;

};

你的代码 3 个错误

错误 1:dp 数组初始化完全写错了!

你写的:

javascript 复制代码
const dp = new Array(m);
for (let i = 0 ; i < n ; i ++){  // ❌ 循环条件错了,应该是 m
    dp[i] = matrix[i];           // ❌ 直接赋值 matrix,会修改原数组!
}

正确逻辑:

  • dp 是独立数组,不能直接等于 matrix
  • 循环应该遍历 m(行数),不是 n(列数)

错误 2:初始化第一行时,把 (0,0) 重复赋值了

虽然不影响结果,但不规范,我帮你修正。

错误 3:当 matrix [i][j] = '0' 时

没有把 dp [i][j] 设为 0 !它会继承原 matrix 的字符 '0',导致 min 计算出 NaN!

修复后的完整正确代码

javascript 复制代码
/**
 * @param {character[][]} matrix
 * @return {number}
 */
var maximalSquare = function(matrix) {
    if(matrix.length == 0 || matrix[0].length == 0 ){
        return 0 ;   
    }

    let m = matrix.length;     // 行数
    let n = matrix[0].length;  // 列数

    // ✅ 正确初始化 dp 数组(独立数组)
    const dp = new Array(m);
    for (let i = 0; i < m; i++) {       // 循环 m 次,不是 n!
        dp[i] = new Array(n).fill(0);   // 新建数组,不污染原矩阵
    }

    let maxSize = 0;

    // ✅ 初始化第一列
    for (let i = 0; i < m; i++) {
        if (matrix[i][0] === '1') {
            dp[i][0] = 1;
            maxSize = 1;
        }
    }

    // ✅ 初始化第一行(跳过(0,0))
    for (let j = 1; j < n; j++) {
        if (matrix[0][j] === '1') {
            dp[0][j] = 1;
            maxSize = 1;
        }
    }

    // ✅ 核心 DP 循环
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            if (matrix[i][j] === '1') {
                dp[i][j] = Math.min(
                    dp[i-1][j],
                    dp[i][j-1],
                    dp[i-1][j-1]
                ) + 1;
                maxSize = Math.max(maxSize, dp[i][j]);
            } else {
                dp[i][j] = 0; // ✅ 必须写!0 位置要置 0
            }
        }
    }

    return maxSize * maxSize;
};

最关键的 2 个坑

1)JavaScript 二维数组不能直接写 dp[i] = matrix[i]

这是引用赋值,不是拷贝!改 dp 等于改原 matrix,直接导致计算全错!

必须这样:

javascript 复制代码
dp[i] = new Array(n).fill(0);

2)循环条件:i < m 行数,j < n 列数

你之前写了

javascript 复制代码
for(let i=0; i < n; i++)

直接越界,程序崩溃!


一句话总结你错在哪

  1. dp 数组初始化方式错误
  2. 循环条件 m/n 写反
  3. 0 没有手动赋值,导致数字和字符混算

你现在把修复版代码复制过去,直接 AC ✅

需要我带你逐行对比你写的和正确版吗?我可以一步步给你讲!

3 题目

123. 买卖股票的最佳时机 III

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

复制代码
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
     随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例 2:

复制代码
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

复制代码
输入:prices = [7,6,4,3,1] 
输出:0 
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

示例 4:

复制代码
输入:prices = [1]
输出:0

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 105

4 代码实现

c++

cpp 复制代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if ( n <= 1 ){
            return 0 ;
        }

        int buy1 = -prices[0];
        int sell1 = 0 ;
        int buy2 = -prices[0];
        int sell2 = 0 ;

        for (int i = 1 ; i < n ; i ++){
            int new_buy1 = max (buy1 , -prices[i]);
            int new_sell1 = max (sell1 , buy1 + prices[i]);
            int new_buy2 = max (buy2 , new_sell1 - prices[i]);
            int new_sell2 = max (sell2 , buy2 + prices[i]);

            buy1 = new_buy1 ;
            sell1 = new_sell1 ;
            buy2 = new_buy2 ;
            sell2 = new_sell2 ;
        }
        return sell2 ;
    }
};

js

javascript 复制代码
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    const n = prices.length ;
    if (n <= 1 ){
        return 0 ; 
    }

    let buy1 = -prices[0] ;
    let sell1 = 0 ;
    let buy2 = -prices[0];
    let sell2 = 0 ;

    for (let i = 1; i < n; i++) {
        const newBuy1 = Math.max(buy1, -prices[i]);
        const newSell1 = Math.max(sell1, buy1 + prices[i]);
        const newBuy2 = Math.max(buy2, sell1 - prices[i]);
        const newSell2 = Math.max(sell2, buy2 + prices[i]);

        buy1 = newBuy1;
        sell1 = newSell1;
        buy2 = newBuy2;
        sell2 = newSell2;
    }

    return Math.max(sell1, sell2);
};

思考

看到这个hard难度我就被吓一大跳啊hhhh...

这咋做啊,这个题目和dp有什么关系,说实话我都不能在数学上找出好的解法,好难。

题解

别怕!这道 Hard 题的核心逻辑非常固定 ,就是动态规划 + 状态机 ,我带你从 0 到 1 完全吃透,看完你会发现它一点都不难!

先给你吃定心丸:这道题不是考数学猜价格 ,而是考有限状态的动态规划 ,只要记住4 个核心状态,直接套模板!


一、先读懂题目(关键!)

  1. 最多可以完成 2 笔交易(可以 1 笔、0 笔,但不能超过 2 笔)
  2. 不能同时持有多只股票(买之前必须先卖)
  3. 求最大利润

二、核心思考:为什么用动态规划?

因为每一天你都有几种明确的状态,状态之间可以转移:

  • 没买过股票
  • 买了第 1 次,还没卖
  • 卖了第 1 次,空仓
  • 买了第 2 次,还没卖
  • 卖了第 2 次,空仓

最终答案一定是「空仓」状态(持有股票不可能利润最大)

这就是状态机 DP ------ 把每天的行为拆成固定状态,用 DP 记录每个状态的最大利润。


三、定义 4 个核心 DP 状态(最重要!)

我们只需要 4 个变量 就能搞定:

  1. buy1 :第 i 天完成第 1 次买入后,手里的最大利润
  2. sell1 :第 i 天完成第 1 次卖出后,手里的最大利润
  3. buy2 :第 i 天完成第 2 次买入后,手里的最大利润
  4. sell2 :第 i 天完成第 2 次卖出后,手里的最大利润

最终答案:sell2(因为卖完 2 次一定是最大利润;卖 1 次也会被包含在 sell2 里)


四、状态转移方程(直接背!)

cpp 复制代码
buy1  = max( 之前的buy1,  -prices[i] )
sell1 = max( 之前的sell1, buy1 + prices[i] )
buy2  = max( 之前的buy2,  sell1 - prices[i] )
sell2 = max( 之前的sell2, buy2 + prices[i] )

我给你逐句翻译成人话

1. buy1(第一次买入)
  • 要么:之前已经买好了,今天不动
  • 要么:今天第一次买 → 利润 = 花掉 prices [i]-prices[i]
2. sell1(第一次卖出)
  • 要么:之前已经卖好了,今天不动
  • 要么:今天卖出第一次买的 → 利润 = buy1 + 今天价格
3. buy2(第二次买入)
  • 要么:之前已经买好第二次了,今天不动
  • 要么:用第一次卖出的钱,今天再买 → sell1 - 今天价格
4. sell2(第二次卖出)
  • 要么:之前已经卖好第二次了,今天不动
  • 要么:卖出第二次买的 → buy2 + 今天价格

五、初始化(第一天怎么来?)

第 0 天(第一天):

  • buy1 = -prices[0] (花钱买入)
  • sell1 = 0 (不能当天卖,利润 0)
  • buy2 = 极小值(还没完成第一笔,不能买第二笔)
  • sell2 = 0 (利润 0)

C++ 用:

cpp 复制代码
int buy1 = -prices[0];
int sell1 = 0;
int buy2 = INT_MIN;
int sell2 = 0;

六、C++ 完整代码(最优解!)

cpp 复制代码
#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n <= 1) return 0;

        // 定义4个状态
        int buy1 = -prices[0];   // 第一次买入
        int sell1 = 0;          // 第一次卖出
        int buy2 = INT_MIN;      // 第二次买入
        int sell2 = 0;          // 第二次卖出

        // 从第2天开始遍历
        for (int i = 1; i < n; i++) {
            // 必须用【上一轮的旧值】更新,所以先存临时变量
            int new_buy1 = max(buy1, -prices[i]);
            int new_sell1 = max(sell1, buy1 + prices[i]);
            int new_buy2 = max(buy2, sell1 - prices[i]);
            int new_sell2 = max(sell2, buy2 + prices[i]);

            // 更新为新值
            buy1 = new_buy1;
            sell1 = new_sell1;
            buy2 = new_buy2;
            sell2 = new_sell2;
        }

        // 答案一定是 sell2(包含0次、1次、2次交易的最大值)
        return sell2;
    }
};

// 测试
int main() {
    vector<int> prices = {3,3,5,0,0,3,1,4};
    Solution sol;
    cout << sol.maxProfit(prices) << endl; // 输出 6 ✅
    return 0;
}

七、为什么要存临时变量?(超级关键!)

你会发现我写了 new_buy1 这种临时变量:

cpp 复制代码
new_buy1 = max(buy1, -prices[i])

不能直接写:

cpp 复制代码
buy1 = max(buy1, -prices[i]);
sell1 = max(sell1, buy1 + prices[i]); // ❌ 这里buy1已经被更新了!

因为:今天的所有状态,都必须用【昨天的状态】计算。直接覆盖会导致数据错乱!


八、复杂度(完美通过 1e5 数据)

  • 时间:O(n) 遍历一次数组
  • 空间:O(1) 只用 4 个变量。这是最优解法,没有之一!

九、带你手动算一遍示例 1(秒懂)

prices = [3,3,5,0,0,3,1,4]

最终:sell1 = 3,sell2 = 3 + 3 = 6。和题目答案完全一致!


十、你之前的困惑解答

你说:

不知道和 DP 有什么关系,数学上也想不出

原因:**这不是找规律,是状态枚举!**把每天的所有可能状态都记录下来,取最大值,就是动态规划。


超级总结(你一定能记住)

  1. 最多 2 次交易 → 4 个状态
  2. buy1 → sell1 → buy2 → sell2
  3. 转移方程固定不变
  4. 答案 = sell2

这道 Hard 题,背会这 4 个状态,直接秒杀!

5 小结

ai题解含量极高,我觉得动规还是有点难,但是必须要抓紧刷了。比较要紧...

哎,思维好混乱啊。

相关推荐
Fly Wine2 小时前
Leetcode之简单题:在区间范围内统计奇数数目
算法·leetcode·职场和发展
CoderCodingNo2 小时前
【GESP】C++五级练习题 luogu-P1102 A-B 数对
开发语言·c++·算法
切糕师学AI2 小时前
深入理解倒排索引(Inverted Index):搜索引擎的核心数据结构
数据结构·搜索引擎·inverted-index
cpp_25012 小时前
B3873 [GESP202309 六级] 小杨买饮料
数据结构·c++·算法·动态规划·题解·洛谷
2301_789015622 小时前
C++11新增特性:可变参数模板、lambda表达式、function包装器、bind绑定、defult和delete
c语言·开发语言·c++·算法·c++11·万能引用
Ahtacca2 小时前
基于决策树算法的动物分类实验:Mac环境复现指南
python·算法·决策树·机器学习·ai·分类
x_xbx2 小时前
LeetCode:567. 字符串的排列
算法·leetcode·职场和发展
沛沛rh452 小时前
力扣 42. 接雨水 - 高效双指针解法(Rust实现)详细题解
算法·leetcode·rust
tankeven3 小时前
HJ158 挡住洪水
c++·算法