523.连续的子数组和

80 阅读1分钟

题目:
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:

  • 子数组大小 至少为 2 ,且
  • 子数组元素总和为 k 的倍数。

如果存在,返回 true ;否则,返回 false 。

如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。0 始终视为 k 的一个倍数。
算法:
方法一:动态规划
dp[i][j]为nums连续子数组取值[i,j]时的和,遍历dp求是否存在dp[i][j] % k == 0 (i < j)

func checkSubarraySum(nums []int, k int) bool {
	n := len(nums)
	dp := make([][]int, n)
	for i := range dp {
		dp[i] = make([]int, n)
		dp[i][i] = nums[i]
	}
	for i := n - 1; i >= 0 ; i -- {
		for j := i + 1; j < n; j ++ {
			dp[i][j] = dp[i][j - 1] + nums[j]
			if dp[i][j] % k == 0 {
				return true
			}
		}
	}
	// fmt.Println(dp)
	return false
}

方法二:动态规划空间压缩
仍然TLE

func checkSubarraySum(nums []int, k int) bool {
	n := len(nums)
	dp := make([]int, n)
	for i := range dp {
		dp[i] = nums[i]
	}
	for i := 0; i < n ; i ++ {
		for j := i + 1; j < n; j ++ {
			dp[i] = dp[i] + nums[j]
			if (k == 0 && dp[i] == 0 )|| (k != 0 && dp[i] % k == 0) {
				return true
			}
		}
	}
	// fmt.Println(dp)
	return false
}

方法三:前缀和 + 哈希表

计算前缀和prefixSum,如果nums [i,j]满足条件,则(prefixSum[j] - prefixSum[i - 1]) % k == 0,意味着prefixSum[j] % k==0 && prefixSum[i - 1]) % k == 0。因此我们计算每个prefixSum对k的余数即可。比较i - 1和j的差是否大于等于2

func checkSubarraySum(nums []int, k int) bool {
	n := len(nums)
	prefixSum := make([]int, n)
	for i := range prefixSum {
		if i > 0 {
			prefixSum[i] = prefixSum[i - 1] + nums[i] 
		} else {
			prefixSum[i] = nums[i]
		}
		
	}
	m := make(map[int]int)
	m[0] = -1
	for i := 0; i < n ; i ++ {
		reminder := prefixSum[i] % k
		if lastIndex, ok := m[reminder]; ok {
			// 前缀和大于等于2,子数组才有2个元组
			if i - lastIndex >= 2 {
				return true
			}
		} else {
			m[reminder] = i
		}
	}

	return false
}