liuxuhelloworld's notebook

题目链接

https://leetcode-cn.com/problems/subarray-product-less-than-k/

解答过程

双指针

对于这个题目,我一开始想到的思路是,对于任意一个nums[i],无论是以它为起点的子数组或者以它为终点的子数组,满足乘积小于K的,数目是确定的,那么对每个nums[i]计算这个数目,再累加起来就可以了。但是如何计算以某个nums[i]开始的满足乘积小于K的子数组的个数呢?我想到的是按子数组长度从小到大挨个乘起来,每累加乘一个,判断一次与K的大小关系。然后直觉上就觉得这样太笨了,看了一下官方题解,官方题解双指针的解法我是觉得代码不大好理解,但是有一点倒是提醒了我,就是并不需要按子数组长度从小到大挨个乘起来,只需要找到以某个num[i]开始的满足乘积小于K的最大的那个子数组就可以了,假设这个最大子数组的结尾下标是j,那么对应的以nums[i]开始的满足乘积小于K的子数组的个数就是j-i+1.

	public int numSubarrayProductLessThanK(int[] nums, int k) {
		int cnt = 0;

		for (int start = 0; start < nums.length; start++) {
			int end = endIndexOfMaxSubarrayProductLessThanKStartFrom(nums, start, k);
			if (end >= start) {
				cnt += end - start + 1;
			}
		}

		return cnt;
	}

	private int endIndexOfMaxSubarrayProductLessThanKStartFrom(int[] nums, int start, int k) {
		int product = 1;
		int i = start;
		for (; i < nums.length; i++) {
			product *= nums[i];
			if (product >= k) {
				break;
			}
		}

		return i - 1;
	}

提交以后是没有问题的,只是效率比较差,但我觉得这个代码比官方题解的好理解。

滑动窗口

官方题解的双指针解法其实更接近滑动窗口的思想,我一开始觉得代码不好理解是缺乏对滑动窗口的理解。这个题目其实和Problem209很像,利用滑动窗口来解答的思路都是,先向右扩展右指针,直到左右指针窗口内的元素不再满足某个要求,开始向右扩展左指针,在这个过程中,维护一个目标计算结果。

	public int numSubarrayProductLessThanK(int[] nums, int k) {
		if (k <= 1) {
			return 0;
		}

		int cnt = 0;

		int left = 0, right = 0, product = 1;
		while (right < nums.length) {
			product *= nums[right];

			while (product >= k) {
				product /= nums[left];
				left++;
			}

			cnt += right - left + 1;

			right++;
		}

		return cnt;
	}