子数组的最大频率分数

Submission

运行时间: 294 ms

内存: 27.8 MB

import collections
class Solution(object):
    def maxFrequencyScore(self, nums, k):
        n = len(nums)
        a_set = collections.defaultdict(int)
        for i in range(k):
            a_set[nums[i]] += 1
        now = 0
        base = 10 ** 9 + 7
        for key, value in a_set.items():
            now += pow(key, value, base)
        now %= base
        max_ans = now
        for i in range(k, n):
            a_set[nums[i - k]] -= 1
            val = a_set[nums[i - k]]
            now = now - pow(nums[i - k], val + 1, base)
            if val > 0:
                now += pow(nums[i - k], val, base)
            a_set[nums[i]] += 1
            val = a_set[nums[i]]
            now = now + pow(nums[i], val, base)
            if val > 1:
                now -= pow(nums[i], val - 1, base)
            now %= base
            max_ans = max(max_ans, now)
        return max_ans

Explain

该题解采用滑动窗口的方法处理子数组的最大频率分数问题。首先,使用一个哈希表a_set记录窗口内各个数的出现次数。接着,定义一个变量now,用以计算当前窗口内各数值的幂和,其中幂的底为数值本身,指数为该数值在窗口中出现的次数,并对结果取一个大质数模以防溢出。初始化时,先处理前k个元素填充窗口,并计算这些元素构成的初始now值。随后,窗口向右滑动,每次移动时,更新哈希表和now值,即从now中减去滑出窗口的数的旧贡献值,并加上新进入窗口的数的新贡献值。同时,用max_ans记录遍历过程中的最大now值。最终,返回max_ans作为结果。

时间复杂度: O(n)

空间复杂度: O(k)

import collections
class Solution(object):
    def maxFrequencyScore(self, nums, k):
        n = len(nums) # 数组长度
        a_set = collections.defaultdict(int) # 记录窗口内数字出现频率
        for i in range(k): # 初始化窗口的频率表
            a_set[nums[i]] += 1
        now = 0 # 当前窗口的数值幂和
        base = 10 ** 9 + 7 # 防止溢出的大质数模
        for key, value in a_set.items(): # 计算初始窗口的now值
            now += pow(key, value, base)
        now %= base
        max_ans = now # 初始最大now
        for i in range(k, n): # 滑动窗口
            a_set[nums[i - k]] -= 1 # 更新窗口,移出一个元素
            val = a_set[nums[i - k]]
            now = now - pow(nums[i - k], val + 1, base) # 移出元素的旧贡献
            if val > 0:
                now += pow(nums[i - k], val, base) # 如果还在窗口内,加上新贡献
            a_set[nums[i]] += 1 # 新元素加入窗口
            val = a_set[nums[i]]
            now = now + pow(nums[i], val, base) # 新元素的贡献
            if val > 1:
                now -= pow(nums[i], val - 1, base) # 减去旧贡献
            now %= base
            max_ans = max(max_ans, now) # 更新最大now值
        return max_ans # 返回最大频率分数

Explore

滑动窗口的大小k通常由问题的具体需求决定,比如在某些问题中,k可能是固定的,或者是由输入的某些参数决定的。窗口大小k直接影响算法的性能:如果k较大,每次滑动窗口时涉及的元素多,更新哈希表和计算幂的时间成本高;如果k较小,则可能无法充分利用数组中的信息,得到的结果可能不是最优。总的来说,k的大小影响着算法的时间复杂度和空间复杂度,以及最终算法能否正确反映数组的统计特性。

在更新哈希表时,如果某个数值的出现频率降至0,理论上这个键值对不再对当前的窗口状态有任何贡献,因此可以从哈希表中移除,以节省存储空间。这不仅可以减少内存占用,还可以在后续操作中减少不必要的查找和更新时间,尤其是在处理大数据集时更为重要。然而,如果频繁地添加和删除操作对性能有较大影响,也可以选择保留这些键值对,特别是在键的总数量相对固定时。

在算法中使用大质数模`10 ** 9 + 7`主要是为了防止计算过程中的整数溢出,并保持结果的稳定性。选择`10 ** 9 + 7`是因为它是一个大的质数,且小于`2^30`,使得乘法操作不会导致32位整数溢出。使用质数作为模数在数学上有好处,如在模数运算下有较好的分布性质,可以减小哈希冲突的概率,从而使得值的分布更均匀。此外,这个数在计算机算法竞赛和实际应用中广泛使用,因其性能表现良好。