力扣每日一题 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=0:index[0]=0 → y=(0+3)%6=3 → 目标模(3-4+6)%6=5(哈希表无)。
    • i=1:index[3]=1 → y=(3+1)%6=4 → 目标模(4-4+6)%6=0(哈希表有,索引 0)→ 长度1-0+1=2 → res=2。
    • i=2:index[4]=2 → y=(4+4)%6=2 → 目标模(2-4+6)%6=4(哈希表有,索引 2)→ 长度2-2+1=1 → res=1。
    • i=3:index[2]=3 → y=(2+2)%6=4 → 目标模(4-4+6)%6=0(哈希表有,索引 0)→ 长度3-0+1=4 → res仍为 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 被广泛使用。

相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
m0_547486663 天前
《数据结构教程》全套 PPT课件2026
数据结构
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark3 天前
从算法设计模式看编程思维的抽象能力4
算法