普通视图

发现新文章,点击刷新页面。
昨天 — 2025年1月18日首页

求出数组中最大序列值

2025年1月3日 09:21

方法一:动态规划

思路

长度为 $2k$ 的序列的值的计算方式为,先将其拆成长度相同的前后子串,然后分别计算各子串的所有元素的按位或的结果,再将两个按位或的结果进行按位异或。为了求出 $\textit{nums}$ 的长度为 $2k$ 的子序列的最大值,可以将 $\textit{nums}$ 分成左右两半,分别求出每一半中,长度为 $k$ 的子序列的按位或的所有可能性,再两两求异或的最大值。这样的分法一共有 $n-2k+1$ 种,其中 $n$ 为数组 $\textit{nums}$ 的长度。并且我们可以用动态规划的方法来求出长度为 $k$ 的子序列的按位或的结果的所有可能性。

记 $\textit{dp}[i][j]$ 为哈希集合,表示 $\textit{nums}[0...i]$ 中长度为 $j$ 的自序列按位或的所有可能性。$j \leq k$,且当 $j$ 为 $0$ 时,$\textit{dp}[i][j]$ 为空集。又根据题目限制,$\textit{nums}$ 元素的值为 $0$ 到 $127$,因此 $\textit{dp}[i][j]$ 最多包含 $128$ 个元素。进行转移时,$\textit{dp}[i][j]$ 首先会包含 $\textit{dp}[i-1][j]$ 的所有元素,表示不选取 $\textit{nums}[i]$。还可以将 $\textit{dp}[i-1][j-1]$ 中的所有元素与 $\textit{nums}[i]$ 进行按位或,将结果添加进 $\textit{dp}[i][j]$,表示选取$\textit{nums}[i]$。我们可以将上述步骤抽象成一个函数,并进一步降低空间复杂度,只返回值 $j=k$ 时的 $dp$值,可以算出左一半的长度为 $k$ 的子序列的按位或的所有可能性。再将 $\textit{nums}$ 进行逆序后再次应用这个函数,即可计算出右一半的的长度为 $k$ 的子序列的按位或的所有可能性。再两两求异或的最大值返回。

我们可以将

代码

###Python

class Solution:
    def maxValue(self, nums: List[int], k: int) -> int:
        def findORs(nums: List[int], k: int) -> List[set[int]]:
            dp = []
            prev = [set() for _ in range(k + 1)]
            prev[0].add(0) 
            for i, num in enumerate(nums):
                for j in range(min(k - 1, i + 1), -1, -1):
                    for x in prev[j]:
                        prev[j + 1].add(x | num)
                dp.append(prev[k].copy())
            return dp

        A = findORs(nums, k)
        B = findORs(nums[::-1], k)

        mx = 0
        for i in range(k - 1, len(nums) - k):
                mx = max(mx, *(a ^ b for a in A[i] for b in B[-i - 2]))
        return mx

###Java

class Solution {
    public int maxValue(int[] nums, int k) {
        List<Set<Integer>> A = findORs(nums, k);
        List<Set<Integer>> B = findORs(reverse(nums), k);
        int mx = 0;
        for (int i = k - 1; i < nums.length - k; i++) {
            for (int a : A.get(i)) {
                for (int b : B.get(nums.length - i - 2)) {
                    mx = Math.max(mx, a ^ b);
                }
            }
        }
        return mx;
    }

    private List<Set<Integer>> findORs(int[] nums, int k) {
        List<Set<Integer>> dp = new ArrayList<>();
        List<Set<Integer>> prev = new ArrayList<>();
        for (int i = 0; i <= k; i++) {
            prev.add(new HashSet<>());
        }
        prev.get(0).add(0);
        for (int i = 0; i < nums.length; i++) {
            for (int j = Math.min(k - 1, i + 1); j >= 0; j--) {
                for (int x : prev.get(j)) {
                    prev.get(j + 1).add(x | nums[i]);
                }
            }
            dp.add(new HashSet<>(prev.get(k)));
        }
        return dp;
    }

    private int[] reverse(int[] nums) {
        int[] reversed = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            reversed[i] = nums[nums.length - 1 - i];
        }
        return reversed;
    }
}

###C#

public class Solution {
    public int MaxValue(int[] nums, int k) {
        var A = FindORs(nums, k);
        Array.Reverse(nums);
        var B = FindORs(nums, k);
        int mx = 0;
        for (int i = k - 1; i < nums.Length - k; i++) {
            foreach (int a in A[i]) {
                foreach (int b in B[nums.Length - i - 2]) {
                    mx = Math.Max(mx, a ^ b);
                }
            }
        }
        return mx;
    }

    public List<HashSet<int>> FindORs(int[] nums, int k) {
        var dp = new List<HashSet<int>>();
        var prev = new List<HashSet<int>>();
        for (int i = 0; i <= k; i++) {
            prev.Add(new HashSet<int>());
        }
        prev[0].Add(0);
        for (int i = 0; i < nums.Length; i++) {
            for (int j = Math.Min(k - 1, i + 1); j >= 0; j--) {
                foreach (int x in prev[j]) {
                    prev[j + 1].Add(x | nums[i]);
                }
            }
            dp.Add(new HashSet<int>(prev[k]));
        }
        return dp;
    }
}

###C++

class Solution {
public:
    int maxValue(vector<int>& nums, int k) {
        auto findORs = [&](const vector<int>& nums, int k) -> vector<unordered_set<int>> {
            vector<unordered_set<int>> dp;
            vector<unordered_set<int>> prev(k + 1);
            prev[0].insert(0);
            for (int i = 0; i < nums.size(); ++i) {
                for (int j = min(k - 1, i + 1); j >= 0; --j) {
                    for (int x : prev[j]) {
                        prev[j + 1].insert(x | nums[i]);
                    }
                }
                dp.push_back(prev[k]);
            }
            return dp;
        };

        vector<unordered_set<int>> A = findORs(nums, k);
        vector<unordered_set<int>> B = findORs(vector<int>(nums.rbegin(), nums.rend()), k);
        int mx = 0;
        for (size_t i = k - 1; i < nums.size() - k; ++i) {
            for (int a : A[i]) {
                for (int b : B[nums.size() - i - 2]) {
                    mx = max(mx, a ^ b);
                }
            }
        }
        return mx;
    }
};

###Go

func maxValue(nums []int, k int) int {
    findORs := func(nums []int, k int) []map[int]bool {
dp := make([]map[int]bool, 0)
prev := make([]map[int]bool, k + 1)
for i := 0; i <= k; i++ {
prev[i] = make(map[int]bool)
}
prev[0][0] = true

for i := 0; i < len(nums); i++ {
for j := min(k - 1, i + 1); j >= 0; j-- {
for x := range prev[j] {
prev[j + 1][x | nums[i]] = true
}
}
current := make(map[int]bool)
for key := range prev[k] {
current[key] = true
}
dp = append(dp, current)
}
return dp
}

A := findORs(nums, k)
reverse(nums)
B := findORs(nums, k)
mx := 0

for i := k - 1; i < len(nums) - k; i++ {
for a := range A[i] {
for b := range B[len(nums) - i - 2] {
if a ^ b > mx {
mx = a ^ b
}
}
}
}
return mx
}

func reverse(nums []int) {
for i, j := 0, len(nums)-1; i < j; i, j = i+1, j-1 {
nums[i], nums[j] = nums[j], nums[i]
}
}

###C

typedef struct {
    int key;
    UT_hash_handle hh;
} HashItem; 

HashItem *hashFindItem(HashItem **obj, int key) {
    HashItem *pEntry = NULL;
    HASH_FIND_INT(*obj, &key, pEntry);
    return pEntry;
}

bool hashAddItem(HashItem **obj, int key) {
    if (hashFindItem(obj, key)) {
        return false;
    }
    HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
    pEntry->key = key;
    HASH_ADD_INT(*obj, key, pEntry);
    return true;
}

void hashFree(HashItem **obj) {
    HashItem *curr = NULL, *tmp = NULL;
    HASH_ITER(hh, *obj, curr, tmp) {
        HASH_DEL(*obj, curr);  
        free(curr);
    }
}

void findORs(int* nums, int numsSize, int k, HashItem** dp) {
    HashItem *prev[k + 1];
    for (int i = 0; i <= k; i++) {
        prev[i] = NULL;
    }
    hashAddItem(&prev[0], 0);
    for (int i = 0; i < numsSize; ++i) {
        for (int j = fmin(k - 1, i + 1); j >= 0; --j) {
            for (HashItem *pEntry = prev[j]; pEntry; pEntry = pEntry->hh.next) {
                int x = pEntry->key;
                hashAddItem(&prev[j + 1], x | nums[i]);
            }
        }
        for (HashItem *pEntry = prev[k]; pEntry; pEntry = pEntry->hh.next) {
            int x = pEntry->key;
            hashAddItem(&dp[i], x);
        }
    }
    for (int i = 0; i <= k; i++) {
        hashFree(&prev[i]);
    }
};

void reverse(int *nums, int numsSize) {
    for (int i = 0, j = numsSize - 1; i < j; i++, j--) {
        int x = nums[i];
        nums[i] = nums[j];
        nums[j] = x;
    }
}

int maxValue(int* nums, int numsSize, int k) {
    HashItem *A[numsSize];
    HashItem *B[numsSize];

    for (int i = 0; i < numsSize; i++) {
        A[i] = B[i] = NULL;
    }
    findORs(nums, numsSize, k, A);
    reverse(nums, numsSize);
    findORs(nums, numsSize, k, B);
    int mx = 0;
    for (size_t i = k - 1; i < numsSize - k; ++i) {
        for (HashItem *pEntry = A[i]; pEntry; pEntry = pEntry->hh.next) {
            int a = pEntry->key;
            for (HashItem *pEntry = B[numsSize - i - 2]; pEntry; pEntry = pEntry->hh.next) {
                int b = pEntry->key;
                mx = fmax(mx, a ^ b);
            }
        }
    }
    for (int i = 0; i < numsSize; i++) {
        hashFree(&A[i]);
        hashFree(&B[i]);
    }
    return mx;
}

###JavaScript

var maxValue = function(nums, k) {
    function findORs(nums, k) {
        const dp = [];
        const prev = Array.from({ length: k + 1 }, () => new Set());
        prev[0].add(0);

        for (let i = 0; i < nums.length; i++) {
            for (let j = Math.min(k - 1, i + 1); j >= 0; j--) {
                for (const x of prev[j]) {
                    prev[j + 1].add(x | nums[i]);
                }
            }
            dp.push(new Set(prev[k]));
        }
        return dp;
    }

    const A = findORs(nums, k);
    nums.reverse();
    const B = findORs(nums, k);
    let mx = 0;

    for (let i = k - 1; i < nums.length - k; i++) {
        for (const a of A[i]) {
            for (const b of B[nums.length - i - 2]) {
                mx = Math.max(mx, a ^ b);
            }
        }
    }
    return mx;
};

###TypeScript

function maxValue(nums: number[], k: number): number {
    function findORs(nums: number[], k: number): Set<number>[] {
        const dp: Set<number>[] = [];
        const prev: Set<number>[] = Array.from({ length: k + 1 }, () => new Set());
        prev[0].add(0);

        for (let i = 0; i < nums.length; i++) {
            for (let j = Math.min(k - 1, i + 1); j >= 0; j--) {
                for (const x of prev[j]) {
                    prev[j + 1].add(x | nums[i]);
                }
            }
            dp.push(new Set(prev[k]));
        }
        return dp;
    }

    const A = findORs(nums, k);
    nums.reverse();
    const B = findORs(nums, k);
    let mx = 0;

    for (let i = k - 1; i < nums.length - k; i++) {
        for (const a of A[i]) {
            for (const b of B[nums.length - i - 2]) {
                mx = Math.max(mx, a ^ b);
            }
        }
    }
    return mx;
};

###Rust

use std::collections::HashSet;
use std::cmp::{max, min};

impl Solution {
    pub fn max_value(nums: Vec<i32>, k: i32) -> i32 {
        fn find_ors(nums: &Vec<i32>, k: i32) -> Vec<HashSet<i32>> {
            let mut dp = Vec::new();
            let mut prev = vec![HashSet::new(); k as usize + 1];
            prev[0].insert(0);

            for i in 0..nums.len() {
                for j in (0..= min(k as usize - 1, i + 1)).rev() {
                    let (before, after) = prev.split_at_mut(j + 1);
                    for &x in &before[j] {
                        after[0].insert(x | nums[i]);
                    }
                }
                dp.push(prev[k as usize].clone());
            }
            dp
        }

        let a = find_ors(&nums, k);
        let reversed_nums: Vec<i32> = nums.iter().rev().cloned().collect();
        let b = find_ors(&reversed_nums, k);
        let mut mx = 0;
        for i in k as usize - 1..nums.len() - k as usize {
            for &a_val in &a[i] {
                for &b_val in &b[nums.len() - i - 2] {
                    mx = mx.max(a_val ^ b_val);
                }
            }
        }
        mx
    }
}

复杂度分析

  • 时间复杂度:$O(n \times k \times U + n \times U^2)$,其中 $n$ 是数组 $\textit{nums}$ 的长度,$U$ 是数组元素的可能性集合大小。$\textit{dp}$ 的状态有 $O(n\times k)$ 种,每个状态消耗 $O(U)$ 计算。最后两两求异或消耗 $O(n\times U^2)$ 的时间。

  • 空间复杂度:$O(n \times U)$。

昨天以前首页

或值至少为 K 的最短子数组 II

2025年1月6日 15:48

方法一:滑动窗口

思路与算法

根据运算的性质可以知道,给定的元素 $A$ 与任意元素 $B$ 进行运算的结果一定满足 $A|B \ge A$,由此可以知道对于任意子数组的长度增加元素后按位运算的结果一定大于等于增加前的结果,满足单调性。由此可知当每次固定数组的右端点时,我们可以使用二分查找或者滑动窗口来找到最短特别子数组的长度。

我们每次用 $[\text{left}, \textit{right}]$ 表示滑动窗口,每次固定右端点 $\textit{right}$,如果当前窗口内子数组按位的结果大于等于 $k$,此时向右移动左端点 $\textit{left}$ 直到窗口内子数组的值按位的结果刚好满足小于 $k$ 为止,此时以 $\textit{right}$ 为右端点的最短特别子数组长度即为 $\textit{right} - \textit{left} + 1$。

为了实时计算当前窗口中子数组运算的结果,我们需要维护二进制位每一位中 $1$ 出现的次数,如果当前位中 $1$ 的出现的次数为 $0$,则按位运算后该位为 $0$,否则该位则为 $1$。由于给定数组 $\textit{nums}$ 中的元素大小不超过 $10^9$ ,因此最多需要考虑二进制表示的前 $30$ 位。我们需要维护一个长度为 $30$ 的数组 $\textit{bits}$,其中 $\textit{bits}[i]$ 表示滑动窗口中满足二进制表示的从低到高第 $i$ 位的值为 $1$ 的元素个数。

具体计算过程如下:

  • 初始时 $\textit{left} = \textit{right} = 0$。每次将滑动窗口的右端点 $\textit{right}$ 向右移动一位,并更新 $\textit{bits}$ 数组,通过计算 $\textit{bits}$ 数组,如果当前窗口内子数组按位的结果大于等于 $k$ 时,窗口内的子数组一定为特别子数组,则尝试应向右移动左端点 $\textit{right}$,并更新 $\textit{bits}$ 数组。

  • 向右移动左端点直到当窗口内子数组按位的结果小于 $k$ 或者 $\textit{left} > \textit{right}$ 时,此时不再移动左端点 $\textit{left}$。在移动窗口的过程中,我们同时更新最短特别子数组长度的长度。

  • 由于可能存在整个数组中所有元素按位的结果小于 $k$,此时不存在特别子数组,此时应返回 $-1$。

代码

###C++

class Solution {
public:
    int minimumSubarrayLength(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> bits(30);
        auto calc = [](vector<int> &bits) -> int {
            int ans = 0;
            for (int i = 0; i < bits.size(); i++) {
                if (bits[i] > 0) {
                    ans |= 1 << i;
                }
            }
            return ans;
        };

        int res = INT_MAX;
        for (int left = 0, right = 0; right < n; right++) {
            for (int i = 0; i < 30; i++) {
                bits[i] += (nums[right] >> i) & 1;
            }
            while (left <= right && calc(bits) >= k) {
                res = min(res, right - left + 1);
                for (int i = 0; i < 30; i++) {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left++;
            }
        }
        
        return res == INT_MAX ? -1: res;
    }
};

###Java

class Solution {
    public int minimumSubarrayLength(int[] nums, int k) {
        int n = nums.length;
        int[] bits = new int[30];
        int res = Integer.MAX_VALUE;
        for (int left = 0, right = 0; right < n; right++) {
            for (int i = 0; i < 30; i++) {
                bits[i] += (nums[right] >> i) & 1;
            }
            while (left <= right && calc(bits) >= k) {
                res = Math.min(res, right - left + 1);
                for (int i = 0; i < 30; i++) {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left++;
            }
        }

        return res == Integer.MAX_VALUE ? -1 : res;
    }

    private int calc(int[] bits) {
        int ans = 0;
        for (int i = 0; i < bits.length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    }
}

###C#

public class Solution {
    public int MinimumSubarrayLength(int[] nums, int k) {
        int n = nums.Length;
        int[] bits = new int[30];
        int res = int.MaxValue;

        for (int left = 0, right = 0; right < n; right++) {
            for (int i = 0; i < 30; i++) {
                bits[i] += (nums[right] >> i) & 1;
            }
            while (left <= right && Calc(bits) >= k) {
                res = Math.Min(res, right - left + 1);
                for (int i = 0; i < 30; i++) {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left++;
            }
        }

        return res == int.MaxValue ? -1 : res;
    }

    private int Calc(int[] bits) {
        int ans = 0;
        for (int i = 0; i < bits.Length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    }
}

###Go

func minimumSubarrayLength(nums []int, k int) int {
    n := len(nums)
bits := make([]int, 30)
res := math.MaxInt32

for left, right := 0, 0; right < n; right++ {
for i := 0; i < 30; i++ {
bits[i] += (nums[right] >> i) & 1
}
for left <= right && calc(bits) >= k {
res = min(res, right - left + 1)
for i := 0; i < 30; i++ {
bits[i] -= (nums[left] >> i) & 1
}
left++
}
}

if res == math.MaxInt32 {
return -1
}
return res
}

func calc(bits []int) int {
ans := 0
for i := 0; i < len(bits); i++ {
if bits[i] > 0 {
ans |= 1 << i
}
}
return ans
}

###Python

class Solution:
    def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
        n = len(nums)
        bits = [0] * 30
        res = inf
        def calc(bits):
            return sum(1 << i for i in range(30) if bits[i] > 0)

        left = 0
        for right in range(n):
            for i in range(30):
                bits[i] += (nums[right] >> i) & 1
            while left <= right and calc(bits) >= k:
                res = min(res, right - left + 1)
                for i in range(30):
                    bits[i] -= (nums[left] >> i) & 1
                left += 1

        return -1 if res == inf else res

###C

int calc(int* bits) {
    int ans = 0;
    for (int i = 0; i < 30; i++) {
        if (bits[i] > 0) {
            ans |= 1 << i;
        }
    }
    return ans;
}

int minimumSubarrayLength(int* nums, int numsSize, int k) {
    int bits[30] = {0};
    int res = INT_MAX;

    for (int left = 0, right = 0; right < numsSize; right++) {
        for (int i = 0; i < 30; i++) {
            bits[i] += (nums[right] >> i) & 1;
        }
        while (left <= right && calc(bits) >= k) {
            res = fmin(res, right - left + 1);
            for (int i = 0; i < 30; i++) {
                bits[i] -= (nums[left] >> i) & 1;
            }
            left++;
        }
    }

    return res == INT_MAX ? -1 : res;
}

###JavaScript

var minimumSubarrayLength = function(nums, k) {
    const n = nums.length;
    const bits = new Array(30).fill(0);
    let res = Number.MAX_SAFE_INTEGER;;

    const calc = (bits) => {
        let ans = 0;
        for (let i = 0; i < bits.length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    };

    let left = 0;
    for (let right = 0; right < n; right++) {
        for (let i = 0; i < 30; i++) {
            bits[i] += (nums[right] >> i) & 1;
        }
        while (left <= right && calc(bits) >= k) {
            res = Math.min(res, right - left + 1);
            for (let i = 0; i < 30; i++) {
                bits[i] -= (nums[left] >> i) & 1;
            }
            left++;
        }
    }

    return res === Number.MAX_SAFE_INTEGER ? -1 : res;
};

###TypeScript

function minimumSubarrayLength(nums: number[], k: number): number {
    const n = nums.length;
    const bits = new Array(30).fill(0);
    let res = Infinity;

    const calc = (bits: number[]): number => {
        let ans = 0;
        for (let i = 0; i < bits.length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    };

    let left = 0;
    for (let right = 0; right < n; right++) {
        for (let i = 0; i < 30; i++) {
            bits[i] += (nums[right] >> i) & 1;
        }
        while (left <= right && calc(bits) >= k) {
            res = Math.min(res, right - left + 1);
            for (let i = 0; i < 30; i++) {
                bits[i] -= (nums[left] >> i) & 1;
            }
            left++;
        }
    }

    return res === Infinity ? -1 : res;
};

###Rust

impl Solution {
    pub fn minimum_subarray_length(nums: Vec<i32>, k: i32) -> i32 {
        let n = nums.len();
        let mut bits = [0; 30];
        let mut res = i32::MAX;

        let calc = |bits: &[i32]| -> i32 {
            let mut ans = 0;
            for i in 0..30 {
                if bits[i] > 0 {
                    ans |= 1 << i;
                }
            }
            ans
        };

        let mut left = 0;
        for right in 0..n {
            for i in 0..30 {
                bits[i] += (nums[right] >> i) & 1;
            }
            while left <= right && calc(&bits) >= k {
                res = res.min((right - left + 1) as i32);
                for i in 0..30 {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left += 1;
            }
        }

        if res == i32::MAX {
            -1
        } else {
            res
        }
    }
}

复杂度分析

  • 时间复杂度:$O(n \log U)$,其中 $n$ 表示给定数组 $\textit{nums}$ 的长度,$U$ 表示数组中的最大的元素。由于使用滑动窗口遍历需要的时间为 $O(n)$,每次更新窗口元素时需要实时计算当前子数组按位的值需要的时间为 $O(\log U)$,此时需要的总时间即为 $O(n \log U)$。

  • 空间复杂度:$O(\log U)$。计算时需要存储当前子数组中每一个二进制位中的统计情况,最多有 $\log U$ 位需要记录,因此需要的空间为 $\log U$。

或值至少 K 的最短子数组 I

2025年1月6日 15:46

方法一:枚举

思路与算法

题目要求找到数组中最短特别非空子数组的长度,即找到最短的子数组且该子数组满足所有元素的按位或运算 $\text{OR}$ 的值大于等于 $k$。我们依次枚举数组中以每个下标 $i$,并找到以 $i$ 为起始位置的最短特别子数组,并记录最短长度即可,如果数组中不存在特别子数组,则返回 $-1$。

代码

###Java

class Solution {
    public int minimumSubarrayLength(int[] nums, int k) {
        int n = nums.length;
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            int value = 0;
            for (int j = i; j < n; j++) {
                value |= nums[j];
                if (value >= k) {
                    res = Math.min(res, j - i + 1);
                    break;
                }
            }
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }
}

###C#

public class Solution {
    public int MinimumSubarrayLength(int[] nums, int k) {
        int n = nums.Length;
        int res = int.MaxValue;
        for (int i = 0; i < n; i++) {
            int value = 0;
            for (int j = i; j < n; j++) {
                value |= nums[j];
                if (value >= k) {
                    res = Math.Min(res, j - i + 1);
                    break;
                }
            }
        }
        return res == int.MaxValue ? -1 : res;
    }
}

###C++

class Solution {
public:
    int minimumSubarrayLength(vector<int>& nums, int k) {
        int n = nums.size();
        int res = INT_MAX;
        for (int i = 0; i < n; i++) {
            int value = 0;
            for (int j = i; j < n; j++) {
                value |= nums[j];
                if (value >= k) {
                    res = min(res, j - i + 1);
                    break;
                }
            }
        }
        return res == INT_MAX ? -1 : res;
    }
};

###Go

func minimumSubarrayLength(nums []int, k int) int {
    n := len(nums)
    res := n + 1
    for i := 0; i < n; i++ {
        value := 0
        for j := i; j < n; j++ {
            value |= nums[j]
            if value >= k {
                res = min(res, j-i+1)
                break
            }
        }
    }
    if res == n + 1 {
        return -1
    }
    return res
}

###Python

class Solution:
    def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
        n = len(nums)
        res = inf
        for i in range(n):
            value = 0
            for j in range(i, n):
                value |= nums[j]
                if value >= k:
                    res = min(res, j - i + 1)
                    break
        return res if res != inf else -1

###C

int minimumSubarrayLength(int* nums, int numsSize, int k) {
    int res = INT_MAX;
    for (int i = 0; i < numsSize; i++) {
        int value = 0;
        for (int j = i; j < numsSize; j++) {
            value |= nums[j];
            if (value >= k) {
                res = (res < j - i + 1) ? res : j - i + 1;
                break;
            }
        }
    }
    return res == INT_MAX ? -1 : res;
}

###JavaScript

var minimumSubarrayLength = function(nums, k) {
    let n = nums.length;
    let res = Number.MAX_VALUE;
    for (let i = 0; i < n; i++) {
        let value = 0;
        for (let j = i; j < n; j++) {
            value |= nums[j];
            if (value >= k) {
                res = Math.min(res, j - i + 1);
                break;
            }
        }
    }
    return res === Number.MAX_VALUE ? -1 : res;
};

###TypeScript

function minimumSubarrayLength(nums: number[], k: number): number {
    const n = nums.length;
    let res = Number.MAX_VALUE;
    for (let i = 0; i < n; i++) {
        let value = 0;
        for (let j = i; j < n; j++) {
            value |= nums[j];
            if (value >= k) {
                res = Math.min(res, j - i + 1);
                break;
            }
        }
    }
    return res === Number.MAX_VALUE ? -1 : res;
};

###Rust

impl Solution {
    pub fn minimum_subarray_length(nums: Vec<i32>, k: i32) -> i32 {
        let n = nums.len();
        let mut res = i32::MAX;
        for i in 0..n {
            let mut value = 0;
            for j in i..n {
                value |= nums[j];
                if value >= k {
                    res = res.min((j - i + 1) as i32);
                    break;
                }
            }
        }
        if res == i32::MAX { -1 } else { res }
    }
}

复杂度分析

  • 时间复杂度:$O(n^2)$,其中 $n$ 表示给定数组 $\textit{nums}$ 的长度。以当前索引 $i$ 为起始位置的子数组元素进行或运算,需要的时间为 $O(n)$,需要枚举每个索引 $i$,一共需要枚举 $n$ 次,因此总的时间为 $O(n^2)$。

  • 空间复杂度:$O(1)$。

方法二:滑动窗口

思路与算法

根据运算的性质可以知道,给定的元素 $A$ 与任意元素 $B$ 进行运算的结果一定满足 $A|B \ge A$,由此可以知道对于任意子数组的长度增加元素后按位运算的结果一定大于等于增加前的结果,满足单调性。由此可知当每次固定数组的右端点时,我们可以使用二分查找或者滑动窗口来找到最短特别子数组的长度。

我们每次用 $[\text{left}, \textit{right}]$ 表示滑动窗口,每次固定右端点 $\textit{right}$,如果当前窗口内子数组按位的结果大于等于 $k$,此时向右移动左端点 $\textit{left}$ 直到窗口内子数组的值按位的结果刚好满足小于 $k$ 为止,此时以 $\textit{right}$ 为右端点的最短特别子数组长度即为 $\textit{right} - \textit{left} + 1$。

为了实时计算当前窗口中子数组运算的结果,我们需要维护二进制位每一位中 $1$ 出现的次数,如果当前位中 $1$ 的出现的次数为 $0$,则按位运算后该位为 $0$,否则该位则为 $1$。由于给定数组 $\textit{nums}$ 中的元素大小不超过 $10^9$ ,因此最多需要考虑二进制表示的前 $30$ 位。我们需要维护一个长度为 $30$ 的数组 $\textit{bits}$,其中 $\textit{bits}[i]$ 表示滑动窗口中满足二进制表示的从低到高第 $i$ 位的值为 $1$ 的元素个数。

具体计算过程如下:

  • 初始时 $\textit{left} = \textit{right} = 0$。每次将滑动窗口的右端点 $\textit{right}$ 向右移动一位,并更新 $\textit{bits}$ 数组,通过计算 $\textit{bits}$ 数组,如果当前窗口内子数组按位的结果大于等于 $k$ 时,窗口内的子数组一定为特别子数组,则尝试应向右移动左端点 $\textit{right}$,并更新 $\textit{bits}$ 数组。

  • 向右移动左端点直到当窗口内子数组按位的结果小于 $k$ 或者 $\textit{left} > \textit{right}$ 时,此时不再移动左端点 $\textit{left}$。在移动窗口的过程中,我们同时更新最短特别子数组长度的长度。

  • 由于可能存在整个数组中所有元素按位的结果小于 $k$,此时不存在特别子数组,此时应返回 $-1$。

代码

###C++

class Solution {
public:
    int minimumSubarrayLength(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> bits(30);
        auto calc = [](vector<int> &bits) -> int {
            int ans = 0;
            for (int i = 0; i < bits.size(); i++) {
                if (bits[i] > 0) {
                    ans |= 1 << i;
                }
            }
            return ans;
        };

        int res = INT_MAX;
        for (int left = 0, right = 0; right < n; right++) {
            for (int i = 0; i < 30; i++) {
                bits[i] += (nums[right] >> i) & 1;
            }
            while (left <= right && calc(bits) >= k) {
                res = min(res, right - left + 1);
                for (int i = 0; i < 30; i++) {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left++;
            }
        }
        
        return res == INT_MAX ? -1: res;
    }
};

###Java

class Solution {
    public int minimumSubarrayLength(int[] nums, int k) {
        int n = nums.length;
        int[] bits = new int[30];
        int res = Integer.MAX_VALUE;
        for (int left = 0, right = 0; right < n; right++) {
            for (int i = 0; i < 30; i++) {
                bits[i] += (nums[right] >> i) & 1;
            }
            while (left <= right && calc(bits) >= k) {
                res = Math.min(res, right - left + 1);
                for (int i = 0; i < 30; i++) {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left++;
            }
        }

        return res == Integer.MAX_VALUE ? -1 : res;
    }

    private int calc(int[] bits) {
        int ans = 0;
        for (int i = 0; i < bits.length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    }
}

###C#

public class Solution {
    public int MinimumSubarrayLength(int[] nums, int k) {
        int n = nums.Length;
        int[] bits = new int[30];
        int res = int.MaxValue;

        for (int left = 0, right = 0; right < n; right++) {
            for (int i = 0; i < 30; i++) {
                bits[i] += (nums[right] >> i) & 1;
            }
            while (left <= right && Calc(bits) >= k) {
                res = Math.Min(res, right - left + 1);
                for (int i = 0; i < 30; i++) {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left++;
            }
        }

        return res == int.MaxValue ? -1 : res;
    }

    private int Calc(int[] bits) {
        int ans = 0;
        for (int i = 0; i < bits.Length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    }
}

###Go

func minimumSubarrayLength(nums []int, k int) int {
    n := len(nums)
bits := make([]int, 30)
res := math.MaxInt32

for left, right := 0, 0; right < n; right++ {
for i := 0; i < 30; i++ {
bits[i] += (nums[right] >> i) & 1
}
for left <= right && calc(bits) >= k {
res = min(res, right - left + 1)
for i := 0; i < 30; i++ {
bits[i] -= (nums[left] >> i) & 1
}
left++
}
}

if res == math.MaxInt32 {
return -1
}
return res
}

func calc(bits []int) int {
ans := 0
for i := 0; i < len(bits); i++ {
if bits[i] > 0 {
ans |= 1 << i
}
}
return ans
}

###Python

class Solution:
    def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
        n = len(nums)
        bits = [0] * 30
        res = inf
        def calc(bits):
            return sum(1 << i for i in range(30) if bits[i] > 0)

        left = 0
        for right in range(n):
            for i in range(30):
                bits[i] += (nums[right] >> i) & 1
            while left <= right and calc(bits) >= k:
                res = min(res, right - left + 1)
                for i in range(30):
                    bits[i] -= (nums[left] >> i) & 1
                left += 1

        return -1 if res == inf else res

###C

int calc(int* bits) {
    int ans = 0;
    for (int i = 0; i < 30; i++) {
        if (bits[i] > 0) {
            ans |= 1 << i;
        }
    }
    return ans;
}

int minimumSubarrayLength(int* nums, int numsSize, int k) {
    int bits[30] = {0};
    int res = INT_MAX;

    for (int left = 0, right = 0; right < numsSize; right++) {
        for (int i = 0; i < 30; i++) {
            bits[i] += (nums[right] >> i) & 1;
        }
        while (left <= right && calc(bits) >= k) {
            res = fmin(res, right - left + 1);
            for (int i = 0; i < 30; i++) {
                bits[i] -= (nums[left] >> i) & 1;
            }
            left++;
        }
    }

    return res == INT_MAX ? -1 : res;
}

###JavaScript

var minimumSubarrayLength = function(nums, k) {
    const n = nums.length;
    const bits = new Array(30).fill(0);
    let res = Number.MAX_SAFE_INTEGER;;

    const calc = (bits) => {
        let ans = 0;
        for (let i = 0; i < bits.length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    };

    let left = 0;
    for (let right = 0; right < n; right++) {
        for (let i = 0; i < 30; i++) {
            bits[i] += (nums[right] >> i) & 1;
        }
        while (left <= right && calc(bits) >= k) {
            res = Math.min(res, right - left + 1);
            for (let i = 0; i < 30; i++) {
                bits[i] -= (nums[left] >> i) & 1;
            }
            left++;
        }
    }

    return res === Number.MAX_SAFE_INTEGER ? -1 : res;
};

###TypeScript

function minimumSubarrayLength(nums: number[], k: number): number {
    const n = nums.length;
    const bits = new Array(30).fill(0);
    let res = Infinity;

    const calc = (bits: number[]): number => {
        let ans = 0;
        for (let i = 0; i < bits.length; i++) {
            if (bits[i] > 0) {
                ans |= 1 << i;
            }
        }
        return ans;
    };

    let left = 0;
    for (let right = 0; right < n; right++) {
        for (let i = 0; i < 30; i++) {
            bits[i] += (nums[right] >> i) & 1;
        }
        while (left <= right && calc(bits) >= k) {
            res = Math.min(res, right - left + 1);
            for (let i = 0; i < 30; i++) {
                bits[i] -= (nums[left] >> i) & 1;
            }
            left++;
        }
    }

    return res === Infinity ? -1 : res;
};

###Rust

impl Solution {
    pub fn minimum_subarray_length(nums: Vec<i32>, k: i32) -> i32 {
        let n = nums.len();
        let mut bits = [0; 30];
        let mut res = i32::MAX;

        let calc = |bits: &[i32]| -> i32 {
            let mut ans = 0;
            for i in 0..30 {
                if bits[i] > 0 {
                    ans |= 1 << i;
                }
            }
            ans
        };

        let mut left = 0;
        for right in 0..n {
            for i in 0..30 {
                bits[i] += (nums[right] >> i) & 1;
            }
            while left <= right && calc(&bits) >= k {
                res = res.min((right - left + 1) as i32);
                for i in 0..30 {
                    bits[i] -= (nums[left] >> i) & 1;
                }
                left += 1;
            }
        }

        if res == i32::MAX {
            -1
        } else {
            res
        }
    }
}

复杂度分析

  • 时间复杂度:$O(n \log U)$,其中 $n$ 表示给定数组 $\textit{nums}$ 的长度,$U$ 表示数组中的最大的元素。由于使用滑动窗口遍历需要的时间为 $O(n)$,每次更新窗口元素时需要实时计算当前子数组按位的值需要的时间为 $O(\log U)$,此时需要的总时间即为 $O(n \log U)$。

  • 空间复杂度:$O(\log U)$。计算时需要存储当前子数组中每一个二进制位中的统计情况,最多有 $\log U$ 位需要记录,因此需要的空间为 $\log U$。

超过阈值的最少操作数 II

2025年1月6日 15:59

方法一: 模拟

思路与算法

本题使用「最小堆」实现的「优先队列」来进行模拟。

首先把所有数加入堆中。当堆顶元素小于 $k$ 时,就把取出堆中最小的两个数,把更新后的数加入堆中。

最后返回操作的数目。

代码

###C++

class Solution {
public:
    int minOperations(vector<int> &nums, int k) {
        int res = 0;
        priority_queue<long long, vector<long long>, greater<long long>> pq(nums.begin(), nums.end());
        while (pq.top() < k) {
            long long x = pq.top(); pq.pop();
            long long y = pq.top(); pq.pop();
            pq.push(x + x + y);
            res++;
        }
        return res;
    }
};

###Java

class Solution {
    public int minOperations(int[] nums, int k) {
        int res = 0;
        PriorityQueue<Long> pq = new PriorityQueue<>();
        for (long num : nums) {
            pq.offer(num);
        }
        while (pq.peek() < k) {
            long x = pq.poll(), y = pq.poll();
            pq.offer(x + x + y);
            res++;
        }
        return res;
    }
}

###Python

class Solution:
    def minOperations(self, nums: List[int], k: int) -> int:
        res = 0
        h = nums[:]
        heapify(h)
        while h[0] < k:
            x = heappop(h)
            y = heappop(h)
            heappush(h, x + x + y)
            res += 1
        return res

###JavaScript

var minOperations = function(nums, k) {
    let res = 0;
    const pq = new MinHeap();

    for (const num of nums) {
        pq.push(num);
    }
    while (pq.peek() < k) {
        const x = pq.pop();
        const y = pq.pop();
        pq.push(x + x + y);
        res++;
    }

    return res;
};

class MinHeap {
    constructor() {
        this.heap = [];
    }

    size() {
        return this.heap.length;
    }

    isEmpty() {
        return this.size() === 0;
    }

    peek() {
        return this.heap[0];
    }

    push(val) {
        this.heap.push(val);
        this._heapifyUp();
    }

    pop() {
        if (this.size() === 1) return this.heap.pop();

        const root = this.heap[0];
        this.heap[0] = this.heap.pop();
        this._heapifyDown();
        return root;
    }

    _heapifyUp() {
        let index = this.size() - 1;
        const element = this.heap[index];

        while (index > 0) {
            const parentIndex = Math.floor((index - 1) / 2);
            const parent = this.heap[parentIndex];

            if (element >= parent) break;

            this.heap[index] = parent;
            this.heap[parentIndex] = element;
            index = parentIndex;
        }
    }

    _heapifyDown() {
        let index = 0;
        const length = this.size();
        const element = this.heap[0];

        while (true) {
            let leftChildIndex = 2 * index + 1;
            let rightChildIndex = 2 * index + 2;
            let smallest = index;

            if (
                leftChildIndex < length &&
                this.heap[leftChildIndex] < this.heap[smallest]
            ) {
                smallest = leftChildIndex;
            }

            if (
                rightChildIndex < length &&
                this.heap[rightChildIndex] < this.heap[smallest]
            ) {
                smallest = rightChildIndex;
            }

            if (smallest === index) break;

            this.heap[index] = this.heap[smallest];
            this.heap[smallest] = element;
            index = smallest;
        }
    }
}

###TypeScript

function minOperations(nums: number[], k: number): number {
    let res = 0;
    const pq = new MinHeap();

    for (const num of nums) {
        pq.push(num);
    }

    while (pq.peek() < k) {
        const x = pq.pop();
        const y = pq.pop();
        pq.push(x + x + y);
        res++;
    }

    return res;
};

class MinHeap {
    private heap: number[];

    constructor() {
        this.heap = [];
    }

    size(): number {
        return this.heap.length;
    }

    isEmpty(): boolean {
        return this.size() === 0;
    }

    peek(): number {
        if (this.isEmpty()) {
            throw new Error("Heap is empty");
        }
        return this.heap[0];
    }

    push(val: number): void {
        this.heap.push(val);
        this._heapifyUp();
    }

    pop(): number {
        if (this.isEmpty()) {
            throw new Error("Heap is empty");
        }
        if (this.size() === 1) {
            return this.heap.pop() as number;
        }

        const root = this.heap[0];
        this.heap[0] = this.heap.pop() as number;
        this._heapifyDown();
        return root;
    }

    private _heapifyUp(): void {
        let index = this.size() - 1;
        const element = this.heap[index];

        while (index > 0) {
            const parentIndex = Math.floor((index - 1) / 2);
            const parent = this.heap[parentIndex];

            if (element >= parent) break;

            this.heap[index] = parent;
            this.heap[parentIndex] = element;
            index = parentIndex;
        }
    }

    private _heapifyDown(): void {
        let index = 0;
        const length = this.size();
        const element = this.heap[0];

        while (true) {
            let leftChildIndex = 2 * index + 1;
            let rightChildIndex = 2 * index + 2;
            let smallest = index;

            if (
                leftChildIndex < length &&
                this.heap[leftChildIndex] < this.heap[smallest]
            ) {
                smallest = leftChildIndex;
            }

            if (
                rightChildIndex < length &&
                this.heap[rightChildIndex] < this.heap[smallest]
            ) {
                smallest = rightChildIndex;
            }

            if (smallest === index) break;

            this.heap[index] = this.heap[smallest];
            this.heap[smallest] = element;
            index = smallest;
        }
    }
}

###Go

func minOperations(nums []int, k int) int {
res := 0
pq := &MinHeap{}
heap.Init(pq)
for _, num := range nums {
heap.Push(pq, num)
}

for (*pq)[0] < k {
x := heap.Pop(pq).(int)
y := heap.Pop(pq).(int)
heap.Push(pq, x+x+y)
res++
}

return res
}

// MinHeap
type MinHeap []int

func (h MinHeap) Len() int           { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h MinHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *MinHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}

func (h *MinHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}

###C#

public class Solution {
    public int MinOperations(int[] nums, int k) {
        int res = 0;
        PriorityQueue<long, long> pq = new PriorityQueue<long, long>();
        foreach (int num in nums) {
            pq.Enqueue(num, num);
        }
        while (pq.Peek() < k) {
            long x = pq.Dequeue(), y = pq.Dequeue();
            pq.Enqueue(x + x + y, x + x + y);
            res++;
        }
        return res;
    }
}

###C

#define MIN_QUEUE_SIZE 64

typedef struct Element {
    long long data[1];
} Element;

typedef bool (*compare)(const void *, const void *);

typedef struct PriorityQueue {
    Element *arr;
    int capacity;
    int queueSize;
    compare lessFunc;
} PriorityQueue;

Element *createElement(int x, int y) {
    Element *obj = (Element *)malloc(sizeof(Element));
    obj->data[0] = x;
    obj->data[1] = y;
    return obj;
}

static bool less(const void *a, const void *b) {
    Element *e1 = (Element *)a;
    Element *e2 = (Element *)b;
    return e1->data[0] > e2->data[0];
}

static bool greater(const void *a, const void *b) {
    Element *e1 = (Element *)a;
    Element *e2 = (Element *)b;
    return e1->data[0] < e2->data[0];
}

static void memswap(void *m1, void *m2, size_t size){
    unsigned char *a = (unsigned char*)m1;
    unsigned char *b = (unsigned char*)m2;
    while (size--) {
        *b ^= *a ^= *b ^= *a;
        a++;
        b++;
    }
}

static void swap(Element *arr, int i, int j) {
    memswap(&arr[i], &arr[j], sizeof(Element));
}

static void down(Element *arr, int size, int i, compare cmpFunc) {
    for (int k = 2 * i + 1; k < size; k = 2 * k + 1) {
        if (k + 1 < size && cmpFunc(&arr[k], &arr[k + 1])) {
            k++;
        }
        if (cmpFunc(&arr[k], &arr[(k - 1) / 2])) {
            break;
        }
        swap(arr, k, (k - 1) / 2);
    }
}

PriorityQueue *createPriorityQueue(compare cmpFunc) {
    PriorityQueue *obj = (PriorityQueue *)malloc(sizeof(PriorityQueue));
    obj->capacity = MIN_QUEUE_SIZE;
    obj->arr = (Element *)malloc(sizeof(Element) * obj->capacity);
    obj->queueSize = 0;
    obj->lessFunc = cmpFunc;
    return obj;
}

void heapfiy(PriorityQueue *obj) {
    for (int i = obj->queueSize / 2 - 1; i >= 0; i--) {
        down(obj->arr, obj->queueSize, i, obj->lessFunc);
    }
}

void enQueue(PriorityQueue *obj, Element *e) {
    // we need to alloc more space, just twice space size
    if (obj->queueSize == obj->capacity) {
        obj->capacity *= 2;
        obj->arr = realloc(obj->arr, sizeof(Element) * obj->capacity);
    }
    memcpy(&obj->arr[obj->queueSize], e, sizeof(Element));
    for (int i = obj->queueSize; i > 0 && obj->lessFunc(&obj->arr[(i - 1) / 2], &obj->arr[i]); i = (i - 1) / 2) {
        swap(obj->arr, i, (i - 1) / 2);
    }
    obj->queueSize++;
}

Element* deQueue(PriorityQueue *obj) {
    swap(obj->arr, 0, obj->queueSize - 1);
    down(obj->arr, obj->queueSize - 1, 0, obj->lessFunc);
    Element *e =  &obj->arr[obj->queueSize - 1];
    obj->queueSize--;
    return e;
}

bool isEmpty(const PriorityQueue *obj) {
    return obj->queueSize == 0;
}

Element* front(const PriorityQueue *obj) {
    if (obj->queueSize == 0) {
        return NULL;
    } else {
        return &obj->arr[0];
    }
}

void clear(PriorityQueue *obj) {
    obj->queueSize = 0;
}

int size(const PriorityQueue *obj) {
    return obj->queueSize;
}

void freeQueue(PriorityQueue *obj) {
    free(obj->arr);
    free(obj);
}

int minOperations(int* nums, int numsSize, int k) {
    int res = 0;
    PriorityQueue *pq = createPriorityQueue(less);
    struct Element e;
    for (int i = 0; i < numsSize; i++) {
        e.data[0] = nums[i];
        enQueue(pq, &e);
    }
    while (front(pq)->data[0] < k) {
        long long x = front(pq)->data[0]; deQueue(pq);
        long long y = front(pq)->data[0]; deQueue(pq);
        e.data[0] = x + x + y;
        enQueue(pq, &e);
        res++;
    }
    freeQueue(pq);
    return res;
}

###Rust

use std::collections::BinaryHeap;
use core::cmp::Reverse;

impl Solution {
    pub fn min_operations(nums: Vec<i32>, k: i32) -> i32 {
        let mut res = 0;
        let mut pq: BinaryHeap<Reverse<i64>> = BinaryHeap::new();
        for &num in &nums {
            pq.push(Reverse(num as i64));
        }
        while let Some(Reverse(x)) = pq.pop() {
            if x >= k as i64 {
                break;
            }
            if let Some(Reverse(y)) = pq.pop() {
                pq.push(Reverse(x + x + y));
                res += 1;
            }
        }
        res
    }
}

复杂度分析

  • 时间复杂度:$O(n\log n)$,其中 $n$ 是数组的长度。

  • 空间复杂度:$O(n)$,其中 $n$ 是数组的长度。

❌
❌