周赛 326 题解

plus2047 于 2023-01-01 发布

上周休假休息一周,本周题解继续!今天有两道质数相关题目,可以分享一下质因数分解和质数表模板。

1.

image

较为简单的一道题目,num 转字符串,其中每个字符测试一下就可以了。

class Solution:
    def countDigits(self, num: int) -> int:
        return sum(1 for c in str(num) if num % int(c) == 0)

2.

image

nums 之积的质因数分解列表,就等于其每一个数字质因数分解列表合并。合并后数一下去重之后的数字个数就可以了。

本题给出一个质因数分解函数模板。这个模板能够在 O(N^0.5) 的时间复杂度下进行质因数分解,而且常数极小,几乎能够对 int32 范围内的任何整数进行分解。模板特殊处理了 2 所以主要循环能够以 2 为步长,加快速度。

template <typename NUM = int>
void prime_fact(NUM num, std::vector<NUM>& res) {
    while (num % 2 == 0) {
        num /= 2;
        res.push_back(2);
    }
    for (int p = 3; p * p <= num; p += 2) {
        while (num % p == 0) {
            res.push_back(p);
            num /= p;
        }
    }
    if (num > 1) res.push_back(num);
}

class Solution {
public:
    int distinctPrimeFactors(vector<int>& nums) {
        vector<int> facts;
        for(int x: nums) {
            prime_fact(x, facts);
        }
        sort(facts.begin(), facts.end());
        int res = 0;
        for(int i = 0; i < int(facts.size()); i++) {
            if(i == 0 or facts[i] != facts[i - 1]) {
                res++;
            }
        }
        return res;
    }
};

3.

image

也是较为简单的一道题目,不难证明,我们可以贪心的进行划分,从左开始,每个分组尽可能的多加入字符。

如果遇到某个字符大于 k 则划分是不可能的。

class Solution:
    def minimumPartition(self, s: str, k: int) -> int:
        res = curr = 0
        for c in s:
            x = int(c)
            if x > k:
                return -1
            if curr * 10 + x > k:
                # 需要另开一组
                res += 1
                curr = 0
            curr = curr * 10 + x
        # 上方的循环最后总是会剩下一组,所以最终结果要加一
        return res + 1

4.

image

这个题目,如果能够求出 left, right 之间所有质数,则是非常简单的。

有一个著名的素数筛算法能够求小于 N 的所有整数中素数列表。这里给出的模板函数,配合 vector<bool> 数据结构,时间和空间效率极高,能够在数秒钟之内求出 1E9 范围内的素数。这个算法的时间复杂度是 O(n) 的,且常数非常小。这个模板函数能暴力过掉一些数据规模很大的题目。

vector<bool> 是 C++ 的一个特殊数据结构,它并不是 vector<> 模板类对 bool 类型的实例化,而是一个特殊实现的类似于 bitmap 的数据结构。它能够以大约 O(N/32) 的速度初始化,并且每个元素只占据一个 bit. 但使用时小心,它并不是一般的 vector<> 它的行为有时候有点古怪。

// this function can get primes less than 1E9 in 4 seconds.
// all flags should be init as true
// assert flags.size() >= 2
void prime_flags(std::vector<bool>& flags) {
    int lim = int(flags.size());
    flags[0] = flags[1] = false;
    for (int n = 4; n < lim; n += 2) {
        flags[n] = false;
    }
    for (int n = 3; n * n <= lim; n += 2) {
        if (flags[n]) {
            for (int t = n * 3; t < lim; t += n << 1) {
                flags[t] = false;
            }
        }
    }
}

class Solution {
public:
    vector<int> closestPrimes(int left, int right) {
        vector<bool> flags(right + 1, true);
        prime_flags(flags);
        int last = -1, mini = right, x = -1, y = -1;
        for(int i = left; i <= right; i++) {
            if(flags[i]) {
                if(last != -1 and i - last < mini) {
                    mini = i - last;
                    x = last, y = i;
                }
                last = i;
            }
        }
        return {x, y};
    }
};