leetcode 第713题 乘积小于K的子数组 python解法

leetcode 第713题 乘积小于K的子数组 python解法

题目解析

leetcode 第713题 乘积小于K的子数组 python解法_第1张图片
开始拿到这个题目的时候,我首先想到的是用动态规划来求解,建立一个二位数组dp,其中dp[i][j]代表的是数组中下标为i到j所有的数累乘的结果,状态转移方程也比较好些,两层循环。外层循环遍历整个数组,内层循环遍历外层循环的值到数组最后一位的所有值。dp[i][j] = dp[i][j-1] * nums[j] (j为内层遍历的值)。
这样很快就写出来了,但是提交时(嘣,瞎卡拉卡)— 空间超了限制,因为有二维数组的存在,所以当给的数组很大时,很容易超过空间限制。
后面我看了一下状态转移方程,当前位置的值只与前面一个位置的值有关,而且每个位置的数只会被用过一次,所以完全可以不用建立二维数组,直接用一个临时变量来存储前一个位置的数,这样空间就降到了常数级别。即用post = prev*nums[j],然后判断post是否大于k,如果大于等于k,立刻结束内层循环。后来又想了一下改进的方法,就是在内层遍历的时,先直接从当前位置向后累乘,直到乘积大于等于k时,结束内层循环。最后将结束的位置j减去刚开始的位置i,并将最后的结果加上返回值ret中(即ret+=j-i)。
就这样我又提交了(嘣,瞎卡拉卡)— 时间超了限制,有一个测试用例里全部都是1,而且很多。1作为因数比较特别。所以我就想先将所以的连续1记录下来,即在外层循环中判断连续1的个数,假设有ones个连续1,那么这些1首先能够组成的连续1的数组个数为(ones+1)xones / 2,这个数直接加上返回数组ret上,然后继续向后遍历。如果前面有连续1的存在,那么在前面的基础上做如下操作(j-i)x(ones+1),这样就把前面1的作用算上了。
这么写最后终于通过了测试,但是还是比较慢,只超过了一半的提交。
我又看了前面运行比较快的解答,豁然开朗,觉得自己写的就是shit。解法如下:
首先在外层循环中将数组的数累乘,每做一乘法后,先判断乘积是否大于k,如果小于k,在返回数组上直接加上该位置的与前面数组成数组的个数(假设当前位置为j,第一个因数的位置为i,那么当前位置能够组成的连续数组个数为j-i+1。如果乘积大于等于k,那么先除去第一个因数,并将i加1,如果仍然大于等于k,继续去除第一个因数(就是i代表的数)。直到乘积小于k或i==j(说明这个数nums[j]就是大于k的),返回外层循环,继续上面的操作。

源码

动态规划

class Solution(object):
    def numSubarrayProductLessThanK(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        n = len(nums)
        ret = 0
        dp = [[nums[i] if i == j else 0 for i in range(n)] for j in range(n)]
        for i in range(n):
            if dp[i][i] < k:
                ret += 1
            for j in range(i+1, n):
                dp[i][j] = dp[i][j-1] * dp[j][j]
                ret += 1 if dp[i][j] < k else 0
        return ret

改进动态规划,并对连续1进行处理

class Solution(object):
    def numSubarrayProductLessThanK(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        ret = 0
        if k > 1:
            n = len(nums)
            ones = 0
            for i in range(n):
                if nums[i] == 1 and i < n:
                    ones += 1
                    continue
                if ones != 0:
                    ret += (1 + ones) * ones // 2
                if nums[i] < k:
                    product = 1
                    j = i
                    while j < n and product * nums[j] < k:
                        product *= nums[j]
                        j += 1
                    ret += (j - i) * (ones + 1) if ones != 0 else j - i
                ones = 0
            if ones != 0:
                ret += (1 + ones) * ones // 2
        return ret

别人写的,最快

class Solution(object):
    def numSubarrayProductLessThanK(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        ret, product, prev = 0, 1, 0
        n = len(nums)
        for i in range(n):
            product *= nums[i]
            while product >= k and prev <= i:
                product //= nums[prev]
                prev += 1
            ret += i - prev + 1
        return ret

写的很乱,今天腊月二十九,有点心不在焉。

leetcode 第713题 乘积小于K的子数组 python解法_第2张图片

你可能感兴趣的:(leetcode解题)