力扣每日一题 2025.11.30

1590.使数组和能被P整除

力扣官方解法,依然是熟悉的哈希+前缀和,老己快点理解这个点吧。。。

  • 核心逻辑 :我们需要找之前的前缀和的模 ,使得 当前前缀和的模 - 之前前缀和的模 ≡ x (mod p)(即移除的子数组的和的模为x)。变形得:之前前缀和的模 ≡ (当前前缀和的模 - x) (mod p)。加p再取模是为了避免负数(比如y-x为负时,结果仍为正的模)。
cpp 复制代码
class Solution {
public:
    int minSubarray(vector<int>& nums, int p) {
        int x = 0;
        //计算数组总和对p取模的结果
        for (auto num : nums) {
            x = (x + num) % p;
        }
        //总体可被整除
        if (x == 0) {
            return 0;
        }
        //初始化哈希表和变量
        //index哈希表用于记录前缀和的模和对应的最后出现的索引
        unordered_map<int, int> index;
        //y:当前前缀和的模(前缀和是数组开头到当前位置的和)
        //res:记录需要移除的最短子数组长度,初始化为数组长度(最大可能的移除长度)
        int y = 0, res = nums.size();
        for (int i = 0; i < nums.size(); i++) {
            //将当前前缀和的模y对应的索引i存入哈希表(覆盖旧值,保证记录的是最后位置)
            index[y] = i;
            //计算新的前缀和(加上当前元素nums[i]),并对p取模,更新y。
            y = (y + nums[i]) % p;
            //如果哈希表中存在这个 "之前的模",说明从index[目标模]+1到i的子数组的和的模是x。
            if (index.count((y - x + p) % p) > 0) {
            //计算该子数组的长度(i - 目标模的索引 + 1),并更新res为最小值。
                res = min(res, i - index[(y - x + p) % p] + 1);
            }
        }
        //如果res仍等于数组长度,说明需要移除整个数组(题目不允许),返回 - 1。
        //否则返回最短子数组的长度res。
        return res == nums.size() ? -1 : res;
    }
};

示例验证(以示例 1 为例)

示例 1:nums = [3,1,4,2], p=6

  • 数组总和的模x = (3+1+4+2) %6 = 10%6=4
  • 遍历过程:
    • i=0index[0]=0y=(0+3)%6=3 → 目标模(3-4+6)%6=5(哈希表无)。
    • i=1index[3]=1y=(3+1)%6=4 → 目标模(4-4+6)%6=0(哈希表有,索引 0)→ 长度1-0+1=2res=2
    • i=2index[4]=2y=(4+4)%6=2 → 目标模(2-4+6)%6=4(哈希表有,索引 2)→ 长度2-2+1=1res=1
    • i=3index[2]=3y=(2+2)%6=4 → 目标模(4-4+6)%6=0(哈希表有,索引 0)→ 长度3-0+1=4res仍为 1。
  • 最终返回1,符合示例 1 的结果。

auto:

在 C++ 中,auto 是一个类型说明符 ,它的核心作用是让编译器自动推断变量的类型,而不需要你显式地写出来。

1. 最基本的用法

当你声明一个变量并立即初始化时,auto 可以根据初始化的值来推断变量的类型。

示例:

cpp 复制代码
// 传统写法,需要显式指定类型
int x = 10;
double y = 3.14;
std::string str = "Hello";

// 使用 auto,编译器自动推断类型
auto a = 10;   // 编译器推断 a 的类型是 int
auto b = 3.14; // 编译器推断 b 的类型是 double
auto c = "Hello"; // 编译器推断 c 的类型是 const char* (在 C++17 及以后,可以是 std::string_view)
auto d = str;  // 编译器推断 d 的类型是 std::string

好处:

  • 代码更简洁 :当变量类型很长或很复杂时,auto 能极大地简化代码。
  • 避免类型错误 :手动写类型容易出错,auto 由编译器推断,更准确。

2. 与迭代器结合(非常常用)

在遍历 STL 容器(如 vector, map, list 等)时,迭代器的类型通常很长。auto 在这里非常有用。

示例:

cpp 复制代码
#include <vector>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // 传统写法
    for (std::vector<int>::iterator it = nums.begin(); it != nums.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 使用 auto 写法,简洁很多
    for (auto it = nums.begin(); it != nums.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // C++11 及以后,配合范围 for 循环,更简洁
    for (auto num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

3. auto dfs = ...

cpp 复制代码
auto dfs = [&](this auto&& dfs, int x, int fa) -> long long { ... };

这里的 auto 有两个作用:

  1. 推断 dfs 变量的类型

    • dfs 变量被赋值为一个lambda 表达式。Lambda 表达式的类型是编译器生成的一个独特的、匿名的函数对象类型。这个类型非常复杂,无法手动写出。
    • 因此,必须使用 auto 来让编译器自动推断 dfs 的类型。
  2. 推断 lambda 捕获的 dfs 的类型(用于递归)

    • 这是一个递归的 lambda 表达式 。为了让 lambda 内部能够调用自身,你需要通过 this auto&& dfs 的方式来捕获它自己。
    • 这里的 auto 同样是让编译器推断被捕获的 dfs 的类型,从而允许递归调用。这是 C++14 引入的特性。

简单来说,在 auto dfs = [&](...) { ... } 这行代码中:

  • auto 告诉编译器:"dfs 的类型由等号右边的表达式决定,你自己去推断吧。"
  • 等号右边是一个 lambda 表达式,所以 dfs 的类型就是这个 lambda 的类型。
  • 由于 lambda 内部需要调用自己,所以捕获列表 [&]this auto&& dfs 一起工作,让这个递归成为可能。

总结

auto 的核心就是类型推断 。它让代码更简洁、更安全,尤其是在处理复杂类型或迭代器时。在现代 C++ 编程中,auto 被广泛使用。

相关推荐
崎岖Qiu34 分钟前
二叉树的非递归后序遍历-双栈法
算法·二叉树·力扣·深度优先遍历·
竹杖芒鞋轻胜马,夏天喜欢吃西瓜38 分钟前
哈希算法解析
算法·哈希算法
执笔论英雄1 小时前
【RL】 ROLL中负载均衡
运维·算法·负载均衡
星辞树1 小时前
从计数到预测:深入浅出词向量 (Word Vectors) —— Stanford CS224n 作业实战记录
算法
JarryStudy1 小时前
自动调优在Triton-on-Ascend中的应用:从参数优化到性能极致挖掘
人工智能·算法·昇腾·cann·ascend c
CoderYanger1 小时前
递归、搜索与回溯-穷举vs暴搜vs深搜vs回溯vs剪枝:13.子集
java·算法·leetcode·机器学习·剪枝·1024程序员节
黑客思维者1 小时前
底层冗余性原理探秘模型剪枝(Pruning)为何能“无损”压缩模型?
算法·机器学习·剪枝
爱学习的小邓同学1 小时前
数据结构 --- 二叉搜索树
数据结构·c++
浪漫血液&1 小时前
索引为什么用B+树而不是B树
数据结构·数据库·b树·b+树