LeetCode 2875.无线数组的最短子数组

给你一个下标从 0 开始的数组 nums 和一个整数 target 。

下标从 0 开始的数组 infinite_nums 是通过无限地将 nums 的元素追加到自己之后生成的。

请你从 infinite_nums 中找出满足 元素和 等于 target 的 最短 子数组,并返回该子数组的长度。如果不存在满足条件的子数组,返回 -1 。

示例 1:

输入:nums = [1,2,3], target = 5
输出:2
解释:在这个例子中 infinite_nums = [1,2,3,1,2,3,1,2,…] 。
区间 [1,2] 内的子数组的元素和等于 target = 5 ,且长度 length = 2 。
可以证明,当元素和等于目标值 target = 5 时,2 是子数组的最短长度。
示例 2:

输入:nums = [1,1,1,2,3], target = 4
输出:2
解释:在这个例子中 infinite_nums = [1,1,1,2,3,1,1,1,2,3,1,1,…].
区间 [4,5] 内的子数组的元素和等于 target = 4 ,且长度 length = 2 。
可以证明,当元素和等于目标值 target = 4 时,2 是子数组的最短长度。
示例 3:

输入:nums = [2,4,6,8], target = 3
输出:-1
解释:在这个例子中 infinite_nums = [2,4,6,8,2,4,6,8,…] 。
可以证明,不存在元素和等于目标值 target = 3 的子数组。

提示:

1 <= nums.length <= 10 5 ^5 5
1 <= nums[i] <= 10 5 ^5 5
1 <= target <= 10 9 ^9 9

如果数组nums中所有元素的和为sum,那么对于target,nums需要重复int(target / sum) + 2次,其中int(target / num)个nums数组中的元素会全部被选为满足题意的子数组,头尾两个nums中,属于结果子数组的部分的元素和为2 * sum - ((int(target / sum) + 2) * sum - target)(将其记为remain),我们只需要保证和为remain的子数组长度最短,直接滑窗即可:

class Solution {
public:
    int minSizeSubarray(vector<int>& nums, int target) {
        long long sum = accumulate(nums.begin(), nums.end(), 0ll);
        int repeatNum = target / sum + 2;
        int remain = 2 * sum - (repeatNum * sum - target);

        int left = 0;
        int curSum = 0;
        int minRemain = numeric_limits<int>::max();
        int ansExist = false;
        for (int i = 0; i < nums.size() * 2; ++i) {
            int realIndex = i % nums.size();
            curSum += nums[realIndex];

            while (curSum > remain) {
                int realLeftIndex = left % nums.size();
                curSum -= nums[realLeftIndex];

                ++left;
            }

            if (curSum == remain) {
                ansExist = true;
                minRemain = min(minRemain, i - left + 1);
            }
        }

        return ansExist ? target / sum * nums.size() + minRemain : -1;
    }
};

如果输入数组nums的长度为n,则此算法时间复杂度为O(n),空间复杂度为O(1)。

你可能感兴趣的:(LeetCode,leetcode,算法,数据结构)