Leetcode.1590 使数组和能被 P 整除

题目链接

Leetcode.1590 使数组和能被 P 整除 rating : 2039

题目描述

给你一个正整数数组 n u m s nums nums,请你移除 最短 子数组(可以为 ),使得剩余元素的 能被 p p p 整除。 不允许 将整个数组都移除。

请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 − 1 -1 1

子数组 定义为原数组中连续的一组元素。

示例 1:

输入:nums = [3,1,4,2], p = 6
输出:1
解释:nums 中元素和为 10,不能被 p 整除。我们可以移除子数组 [4] ,剩余元素的和为 6 。

示例 2:

输入:nums = [6,3,5,2], p = 9
输出:2
解释:我们无法移除任何一个元素使得和被 9 整除,最优方案是移除子数组 [5,2] ,剩余元素为 [6,3],和为 9 。

提示3:

输入:nums = [1,2,3], p = 3
输出:0
解释:和恰好为 6 ,已经能被 3 整除了。所以我们不需要移除任何元素。

提示4:

输入:nums = [1,2,3], p = 7
输出:-1
解释:没有任何方案使得移除子数组后剩余元素的和被 7 整除。

提示5:

输入:nums = [1000000000,1000000000,1000000000], p = 3
输出:0

提示:
  • 1 ≤ n u m s . l e n g t h ≤ 1 0 5 1 \leq nums.length \leq 10^5 1nums.length105
  • 1 ≤ n u m s [ i ] ≤ 1 0 9 1 \leq nums[i] \leq 10^9 1nums[i]109
  • 1 ≤ p ≤ 1 0 9 1 \leq p \leq 10^9 1p109

解法:前缀和 + 哈希表

假设整个数组 n u m s nums nums 的和为 s s s,那么 k = s   m o d   p k = s\ mod\ p k=s mod p

  • 如果 k = 0 k = 0 k=0,说明整个数组的和都可以被 p p p 整除,所以不需要移除元素,直接返回 0 0 0
  • 如果 k ≠ 0 k \neq 0 k=0假设我们需要移除的这个子数组和为 t t t,那么 t   m o d   p = k t\ mod\ p = k t mod p=k,并且还要要求这个子数组的长度是最短的。

假设区间 [ j , i ] [j,i] [j,i] 的子数组满足这个条件,我们用 s u m sum sum 表示 n u m s nums nums 的前缀和,即:

( s u m [ i ] − s u m [ j − 1 ] )   m o d   p = k (sum[i] - sum[j-1])\ mod\ p = k (sum[i]sum[j1]) mod p=k

再转换一下:

s u m [ j − 1 ]   m o d   p = s u m [ i ]   m o d   p − k sum[j-1]\ mod\ p = sum[i]\ mod\ p - k sum[j1] mod p=sum[i] mod pk

由于 s u m [ j ]   m o d   p − k sum[j]\ mod\ p - k sum[j] mod pk 有可能是负数,所以我们需要再将其转换为整数:

s u m [ j − 1 ]   m o d   p = ( s u m [ i ]   m o d   p − k + p )   m o d   p sum[j-1]\ mod\ p = (sum[i]\ mod\ p - k + p)\ mod\ p sum[j1] mod p=(sum[i] mod pk+p) mod p

我们用哈希表来记录这个 s u m [ i ]   m o d   p sum[i]\ mod\ p sum[i] mod p,值就是其对应的下标 i i i

我们令 k e y = ( s u m [ i ]   m o d   p − k + p )   m o d   p key = (sum[i]\ mod\ p - k + p)\ mod\ p key=(sum[i] mod pk+p) mod p,如果对于当前 k e y key key,哈希表 m p mp mp 中有记录,则 j = m p [ k e y ] j = mp[key] j=mp[key]。说明移除此时的子数组 [ j , i ] [j,i] [j,i] 就能使 n u m s nums nums 的剩余元素满足条件,那么我们更新答案 a n s ans ans

注意:

  • 哈希表 m p mp mp 初始时需要加入 { 0 , − 1 } \{0 , -1 \} {0,1}
  • a n s ans ans 是最终的答案,需要移除的最短子数组的长度,初始化为一个较大的数即可;

时间复杂度: O ( n ) O(n) O(n)

C++代码:

using LL = long long;

class Solution {
public:
    int minSubarray(vector<int>& nums, int p) {
        int n = nums.size();
        LL sum = 0;

        for(auto x:nums) sum += x;
        int k = sum % p;

        if(k == 0) return 0;

        unordered_map<int,int> mp{{0 , -1}};

        int ans = n;
        sum = 0;
        for(int i = 0;i < n;i++){
            sum += nums[i];
            auto key = (sum % p - k + p)%p;
            if(mp.find(key) != mp.end()){
                auto j = mp[key];
                ans = min(ans , i - j);
            }
    
            mp[sum % p] = i;
        }
        return ans == n ? -1 : ans;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值