2
365bet押和技巧
热点资讯
微头像体现人品: 慎交三类人, 保持正能
人与人之间的交往,很多时候是一面镜子,从彼此的表现中,我们能照见对方真实的一面。...
新闻动态 你的位置:365bet押和技巧 > 新闻动态 > 2025-12-03: 计数质数间隔平衡子数组。用go语言, 给定一个整数数
2025-12-03: 计数质数间隔平衡子数组。用go语言, 给定一个整数数 发布日期:2025-12-15 12:35    点击次数:106

2025-12-03:计数质数间隔平衡子数组。用go语言,给定一个整数数组 nums 和一个整数 k。请计算数组中有多少个连续且非空的子序列满足下面两个条件:

1. 子序列中至少包含两个质数;

2. 该子序列里所有质数中的最大值与最小值之差不超过 k。

这里“子序列”指的是数组中位置相邻的一段元素(即常说的子数组)。质数指大于 1 且只有 1 和自身两个约数的正整数。返回符合上述条件的子序列数量。

1

1

0

输入:nums = [2,3,5,7], k = 3。

输出:4。

解释:

质数间隔平衡子数组有:

[2,3]:包含 2 个质数(2 和 3),最大值 - 最小值 = 3 - 2 = 1

[2,3,5]:包含 3 个质数(2,3 和 5),最大值 - 最小值 = 5 - 2 = 3

[3,5]:包含 2 个质数(3 和 5),最大值 - 最小值 = 5 - 3 = 2

[5,7]:包含 2 个质数(5 和 7),最大值 - 最小值 = 7 - 5 = 2

因此,答案为 4。

题目来自力扣3589。

算法过程详解

该Go语言程序用于统计满足特定条件的连续子数组(子序列)数量。以下是大体过程的详细分步描述:

1. 质数预计算(初始化阶段)

• 程序使用埃拉托斯特尼筛法预计算所有小于50,001的质数。全局数组np用于标记非质数(np[i]为true表示i不是质数)。

• 筛法从2开始遍历到√mx,对每个质数i,标记其所有倍数(从i*i开始)为非质数。这一步确保在后续处理中能以O(1)时间判断任意数是否为质数。

2. 滑动窗口遍历主逻辑

函数primeSubarray使用双指针(滑动窗口) 结合单调队列来动态维护窗口内的质数极值:

• 初始化:

• left:窗口左边界,初始为0。

• last和last2:分别记录当前窗口内最近一个和倒数第二个质数的位置,初始为-1。

• minQ和maxQ:单调队列(双端队列),分别维护当前窗口内质数索引的递增(最小值队列)和递减(最大值队列)顺序。

• 遍历数组(右指针扩张):

• 对每个元素nums[i],若它是质数(!np[x]为真):

1. 更新质数位置:

last2记录上一个质数位置(last),last更新为当前索引i。这确保窗口内至少有两个质数时,last2有效。

2. 维护单调队列:

最小值队列minQ:从队尾移除所有对应质数大于等于当前质数的索引,保持队列严格递增。

最大值队列maxQ:从队尾移除所有对应质数小于等于当前质数的索引,保持队列严格递减。

然后将当前索引i加入两队。

3. 调整窗口左边界(收缩):

若当前窗口内最大质数与最小质数之差(即nums[maxQ[0]] - nums[minQ[0]])超过k,则右移左边界left。同时,清理minQ和maxQ中队首小于left的索引(这些索引已离开窗口)。

• 统计有效子数组:

每处理一个元素后(无论是否为质数),若窗口内至少有两个质数(last2 ≠ -1),则计算以i结尾的有效子数组数量:

左边界范围是[left, last2],因为子数组必须包含last2和last两个质数。因此,答案增加last2 - left + 1。

3. 结果返回

• 遍历结束后,返回累加值ans,即所有满足条件的子数组数量。

复杂度分析

• 时间复杂度:

质数筛预计算:O(mx log log mx) ≈ O(50,000 log log 50,000),可视为常数级。

主遍历:数组长度为n,每个元素最多被加入/移除单调队列一次,均摊操作O(1)。因此总时间复杂度为 O(n)。

• 空间复杂度:

固定数组np占用O(mx) ≈ O(50,000),为常数空间。

单调队列minQ和maxQ最坏情况下存储O(n)个索引。因此总额外空间复杂度为 O(n)。

关键点总结

• 通过质数筛实现O(1)质数判断,避免每次重复计算。

• 单调队列高效维护滑动窗口内的极值,确保质数差约束的实时检查。

• 利用last2记录倒数第二个质数位置,直接确定有效左边界范围,避免暴力枚举。

Go完整代码如下:

package main

import (

"fmt"

)

const mx = 50_001

var np = [mx]bool{1: true} // 1 不是质数

func init {

for i := 2; i*i

if !np[i] {

for j := i * i; j

np[j] = true

}

}

}

}

func primeSubarray(nums []int, k int) (ans int) {

var minQ, maxQ []int

last, last2 := -1, -1

left := 0

for i, x := range nums {

if !np[x] {

// 1. 入

last2 = last

last = i

forlen(minQ) > 0 && x

minQ = minQ[:len(minQ)-1]

}

minQ = append(minQ, i)

forlen(maxQ) > 0 && x >= nums[maxQ[len(maxQ)-1]] {

maxQ = maxQ[:len(maxQ)-1]

}

maxQ = append(maxQ, i)

// 2. 出

for nums[maxQ[0]]-nums[minQ[0]] > k {

left++

if minQ[0]

minQ = minQ[1:]

}

if maxQ[0]

maxQ = maxQ[1:]

}

}

}

// 3. 更新答案

ans += last2 - left + 1

}

return

}

func main {

nums := []int{2, 3, 5, 7}

k := 3

result := primeSubarray(nums, k)

fmt.Println(result)

}

Python完整代码如下:

# -*-coding:utf-8-*-

from typing import List

from collections import deque

# 预处理质数

MX = 50001

np = [False] * MX

np[0] = np[1] = True # 0 和 1 不是质数

for i in range(2, int(MX ** 0.5) + 1):

if not np[i]:

for j in range(i * i, MX, i):

np[j] = True

def primeSubarray(nums: List[int], k: int) -> int:

ans = 0

min_q = deque # 单调递增队列(最小值的索引)

max_q = deque # 单调递减队列(最大值的索引)

last = -1 # 最近一个质数的索引

last2 = -1 # 倒数第二个质数的索引

left = 0 # 窗口左边界

for i, x in enumerate(nums):

if not np[x]: # x 是质数

# 1. 更新质数索引

last2 = last

last = i

# 维护最小值的单调递增队列

while min_q and x

min_q.pop

min_q.append(i)

# 维护最大值的单调递减队列

while max_q and x >= nums[max_q[-1]]:

max_q.pop

max_q.append(i)

# 2. 调整窗口左边界,确保窗口内最大最小差值

while nums[max_q[0]] - nums[min_q[0]] > k:

left += 1

if min_q[0]

min_q.popleft

if max_q[0]

max_q.popleft

# 3. 更新答案(累加以当前元素结尾的有效子数组数量)

ans += max(0, last2 - left + 1)

return ans

if __name__ == "__main__":

nums = [2, 3, 5, 7]

k = 3

result = primeSubarray(nums, k)

print(result) # 输出结果

C++完整代码如下:

#include

#include

#include

#include

using namespace std;

constint MX = 50001;

bool np[MX] = {false};

// 初始化质数筛

void init {

np[0] = np[1] = true; // 0和1不是质数

for (int i = 2; i * i

if (!np[i]) {

for (int j = i * i; j

np[j] = true;

}

}

}

}

int primeSubarray(vector& nums, int k) {

int ans = 0;

deque minQ; // 单调递增队列(最小值的索引)

deque maxQ; // 单调递减队列(最大值的索引)

int last = -1; // 最近一个质数的索引

int last2 = -1; // 倒数第二个质数的索引

int left = 0; // 窗口左边界

for (int i = 0; i

int x = nums[i];

if (!np[x]) { // x是质数

// 1. 更新质数索引

last2 = last;

last = i;

// 维护最小值的单调递增队列

while (!minQ.empty && x

minQ.pop_back;

}

minQ.push_back(i);

// 维护最大值的单调递减队列

while (!maxQ.empty && x >= nums[maxQ.back]) {

maxQ.pop_back;

}

maxQ.push_back(i);

// 2. 调整窗口左边界,确保窗口内最大最小差值

while (nums[maxQ.front] - nums[minQ.front] > k) {

left++;

if (!minQ.empty && minQ.front

minQ.pop_front;

}

if (!maxQ.empty && maxQ.front

maxQ.pop_front;

}

}

}

// 3. 更新答案(累加以当前元素结尾的有效子数组数量)

if (last2 >= left) {

ans += last2 - left + 1;

}

}

return ans;

}

int main {

// 初始化质数筛

init;

vector nums = {2, 3, 5, 7};

int k = 3;

int result = primeSubarray(nums, k);

cout

return0;

}

我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。

欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。