2026/3/14:
由于最近做了没来得及写详细题解,这里先把代码附上来,之后复习的时候再写。
LeetCode 1248:
题目

分析
前缀和+哈希
代码
cpp
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int n = nums.size();
unordered_map<int,int>dp;
dp[0] = 1;
int sum = 0;
int ans = 0;
for(int i = 0 ;i<n;i++){
if(nums[i]%2!=0){
sum++;
}
int need = sum - k;
if(dp.find(need)!=dp.end()){
ans += dp[need];
}
dp[sum]++;
}
return ans;
}
};
结果

LeetCode 121:
题目

分析
看的代码随想录的视频,动态规划,主要是dp数组的定义不容易想到。
代码
cpp
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<vector<int>>dp(n,vector<int>(2,0));
dp[0][0] = -prices[0];
dp[0][1] = 0;
for(int i = 1;i < n;i++){
dp[i][0] = max(-prices[i],dp[i-1][0]);
dp[i][1] = max(prices[i]+dp[i-1][0],dp[i-1][1]);
}
return dp[n-1][1];
}
};
结果

LeetCode 43:
题目

分析
其实就是大数乘法,想要转换为整型再乘得到答案,然后再转回字符串的话,大数会整型溢出。所以采用"竖式乘法"的方法。
代码
cpp
class Solution {
public:
string multiply(string num1, string num2) {
if (num1 == "0" || num2 == "0") return "0";
int m = num1.size(), n = num2.size();
vector<int> res(m + n, 0);
// 从右往左模拟乘法
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
int mul = (num1[i] - '0') * (num2[j] - '0');
int p1 = i + j, p2 = i + j + 1; // p2 是低位,p1 是高位
int sum = mul + res[p2];
res[p2] = sum % 10;
res[p1] += sum / 10;
}
}
// 转为字符串,跳过前导零
string ans = "";
int i = 0;
while (i < res.size() && res[i] == 0) i++;
for (; i < res.size(); i++) {
ans += (res[i] + '0');
}
return ans.empty() ? "0" : ans;
}
};
结果

LeetCode 93:
题目

分析
想到高中数学统计学的插板法求组和数,大概思路就是用插板法得到所有可能的组合,然后再判断每个组合是否合法。
代码
cpp
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
int n = s.length();
vector<string>res;
if(n < 4 || n>12) return res;
// 辅助 lambda:判断一段是否合法
auto isValid = [](const string& seg) -> bool {
if (seg.empty() || seg.size() > 3) return false;
if (seg[0] == '0' && seg.size() > 1) return false; // 前导零
// 检查是否全为数字(题目保证输入是数字,可省略,但保留更健壮)
for (char c : seg) {
if (!isdigit(c)) return false;
}
int num = stoi(seg);
return num >= 0 && num <= 255;
};
// 枚举三个分割点 i, j, k
// s[0:i), s[i:j), s[j:k), s[k:n)
for (int i = 1; i <= 3 && i < n - 2; ++i) { // 第一段最多取3位,且后面至少留3个字符
for (int j = i + 1; j <= i + 3 && j < n - 1; ++j) { // 第二段最多3位,后面至少留2个
for (int k = j + 1; k <= j + 3 && k < n; ++k) { // 第三段最多3位,后面至少留1个
string a = s.substr(0, i);
string b = s.substr(i, j - i);
string c = s.substr(j, k - j);
string d = s.substr(k, n - k);
if (isValid(a) && isValid(b) && isValid(c) && isValid(d)) {
res.push_back(a + "." + b + "." + c + "." + d);
}
}
}
}
return res;
}
};
结果

LeetCode 62:
题目

分析
同样是经典的动态规划问题,感觉就是二维的爬楼梯,方向固定为两个,dp[i][j]的方法来源于它的上方和左方,也就是
dp[i][j] = dp[i-1][j]+dp[i][j-1]
当然,要注意边界条件。
代码
cpp
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>>dp(m,vector<int>(n,0));//到(i,j)有几种方法
for(int i = 0;i < m;i++){
for(int j = 0;j < n;j++){//第一列和第一行都为1,因为只能有左边或者上边来
if( i == 0 || j == 0)dp[i][j] = 1;
else
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
结果

LeetCode 63:
题目

分析
和1比就是加入了障碍物,有了障碍物后,在初始化的时候,障碍物这里应该为0不变并且如果是第一列或者第一行位于障碍物及其后续位置(比如第一列在障碍物下面的或者第一行在障碍物右边的)也应该始终为0。后续遍历当到达障碍物时,直接跳过。
代码
cpp
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
vector<vector<int>>dp(m,vector<int>(n,0));
//初始化跳过障碍物及其后续位置
for(int i = 0;i < m&&obstacleGrid[i][0] == 0;i++) dp[i][0] = 1;
for(int i = 0;i < n&&obstacleGrid[0][i] == 0;i++) dp[0][i] = 1;
for(int i = 1;i < m;i++){
for(int j = 1;j < n;j++){
if(obstacleGrid[i][j]==1) continue;//跳过障碍
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
结果
