2025-10-11:求出数组的 X 值Ⅰ。用go语言,给定一个只包含正整数的数组 nums 和一个正整数 k。
你可以进行一次删除操作:在数组两端各自选取一段连续元素删掉(这两段不能相互重叠),删除后数组必须至少保留一个元素。两端要删的那段可以为空(即可以只删左端、只删右端或都不删),但不能删掉整个数组。
对每一种合法的删除方式,计算剩下的那段元素的乘积对 k 取模后的值 x。定义 nums 的 x 值为能得到该模值 x 的删除方式数量。
请输出一个长度为 k 的数组 result,其中 result[x](0 ≤ x ≤ k−1)表示得到模值 x 的删除方式数。
1 <= nums[i] <= 1000000000。
1 <= nums.length <= 100000。
1 <= k <= 5。
输入: nums = [1,1,2,1,1], k = 2。
输出: [9,6]。
题目来自力扣3524。
算法过程分步描述
1. 初始化阶段
- 创建一个长度为
k
的结果数组ans
,初始化为全0,用于存储每个模值x
对应的删除方式数量 - 创建一个长度为
k
的状态数组f
,初始化为全0,用于动态记录当前所有可能乘积模k
的路径数量
2. 遍历数组处理每个元素
对于数组 nums
中的每个元素 v
:
2.1 创建新的状态数组
- 创建一个新的状态数组
nf
,长度为k
,初始化为全0
2.2 处理当前元素单独形成子数组的情况
- 计算当前元素
v
对k
取模的值:v % k
- 在
nf
中对应的位置计数加1,表示当前元素单独作为一个子数组的情况
2.3 基于之前的状态更新新状态
- 遍历之前状态数组
f
中的所有模值y
及其对应的计数c
- 对于每个
(y, c)
对,计算新的模值:(y * v) % k
- 将之前的计数
c
累加到新状态nf
中对应的模值位置上 - 这表示将当前元素
v
与之前所有可能的子数组乘积进行组合
2.4 更新状态和结果
- 将旧状态
f
更新为新状态nf
- 将新状态
nf
中的所有计数累加到结果数组ans
的对应位置上
3. 算法逻辑解释
- 该算法实际上是在动态统计所有可能的连续子数组(通过删除两端得到)的乘积模
k
值的分布 - 每次处理新元素时,考虑:
- 该元素单独作为一个子数组
- 该元素与之前所有子数组组合形成新的子数组
- 状态数组
f
记录了到当前位置为止,所有可能子数组乘积模k
值的分布情况
4. 示例分析(nums = [1,1,2,1,1], k = 2)
- 最终得到结果:[9,6]
- 表示模值为0的删除方式有9种,模值为1的删除方式有6种
- 这与题目描述的输出一致
复杂度分析
总的时间复杂度:O(n × k)
- 需要遍历数组中的 n 个元素
- 对于每个元素,需要遍历长度为 k 的状态数组
- 由于 k ≤ 5,实际可以视为 O(n)
总的额外空间复杂度:O(k)
- 只需要维护两个长度为 k 的状态数组(f 和 nf)
- 结果数组 ans 的长度也为 k
- 空间使用与输入数组大小 n 无关
Go完整代码如下:
go
package main
import (
"fmt"
)
func resultArray(nums []int, k int) []int64 {
ans := make([]int64, k)
f := make([]int, k)
for _, v := range nums {
nf := make([]int, k)
nf[v%k] = 1
for y, c := range f {
nf[y*v%k] += c
}
f = nf
for x, c := range f {
ans[x] += int64(c)
}
}
return ans
}
func main() {
nums := []int{1, 1, 2, 1, 1}
k := 2
result := resultArray(nums, k)
fmt.Println(result)
}

Python完整代码如下:
python
# -*-coding:utf-8-*-
def result_array(nums, k):
ans = [0] * k
f = [0] * k
for v in nums:
nf = [0] * k
nf[v % k] = 1
for y, c in enumerate(f):
nf[(y * v) % k] += c
f = nf
for x, c in enumerate(f):
ans[x] += c
return ans
if __name__ == "__main__":
nums = [1, 1, 2, 1, 1]
k = 2
result = result_array(nums, k)
print(result)
