Python solution leetcode no.1567

Time:2021-10-24

Leetcode random question: the length of the longest subarray whose product is a positive number. It is an array subject with medium difficulty.

Please stamp the original question here:
1567. Longest subarray length with positive product

Title Description

Give you an integer array nums, please find itThe product is a positive numberofLongestThe length of the subarray. The subarray of an array is an array composed of zero or more consecutive numbers in the original array. Please return the length of the longest subarray whose product is a positive number.

  • Example 1: input: num = [1, – 2, – 3,4] output: 4 explanation: the product of the array itself is a positive number, and the value is 24.
  • Example 2: input: num = [0,1, – 2, – 3, – 4] output: 3 explanation: the subarray with the longest positive product is [1, – 2, – 3], and the product is 6. Note that we can’t include 0 in the subarray, because the product is 0, not a positive number.
  • Example 3: input: num = [- 1, – 2, – 3,0,1] output: 2 explanation: the longest subarray whose product is a positive number is [- 1, – 2] or [- 2, – 3].
  • Example 4: input: num = [- 1,2] output: 1
  • Example 5: input: nums = [1,2,3,5, – 6,4,0,10] output: 4
  • In the test, it is found that the performance test example used by leetcode: the array with length of 100000 is 1 except that num [3000] is – 1.

Problem solving ideas

Problem focus:

  1. An array that does not contain 0,Is the product a positive number, depending on the number of negative numbers. When negative numbers are even, the product of the array is positive.
  2. Whether the product is a positive number has nothing to do with the absolute value of the number, and has nothing to do with the number in the arraySymbols of numbersSo you can convert all positive numbers to 1 and all negative numbers to – 1.
  3. The title requires an array of continuous numbers, so the place where 0 appears must be naturally cut and separated. And cut apartIf there is a unique – 1 in the subarray, which means that the product of this subarray is negative (other numbers are 1 or no other numbers), and it needs to be cut from the position of – 1.
  4. When traversing the subarray to find the length of the qualified subarray, first sort according to the length of the array (Python sorting is very convenient),Start with the longest array, you can end the traversal as soon as possible.

The problem solving process is shown in the figure below:
Python solution leetcode no.1567


First, write some methods to realize each small function point

1. format_ Nums method of converting numbers. The input n is a number, the positive number is converted to 1, the negative number is converted to – 1, and 0 remains unchanged
def format_nums(n):
    if n > 0:
        return 1
    if n < 0:
        return -1
    return 0
2. Cut the molecular array, first by 0, and then by the unique – 1.

The first is the called cut_ by_ single_ Negative method, which is responsible for checking whether there is only one – 1 in the array. If so, cut the array apart and unify the return value into the list of list.

#The subarray has no 0. Check whether there is a unique - 1
#If there is only one - 1, it means that the product of this subarray must be negative and needs to be segmented again
#If there are multiple negative numbers, there is no way
def cut_by_single_negative(sub_ls):
    ret_ls = []
    if sub_ls.count(-1) == 1:
        idx = sub_ls.index(-1)
        ret_ls.append(sub_ls[:idx])
        ret_ls.append(sub_ls[idx+1:])
    else:
        ret_ls.append(sub_ls) 
    return ret_ls

You can verify this method:

>>>cut_by_single_negative([1, 1, 1, 1, -1, 1])
[[1, 1, 1, 1], [1]]

Then there is the outer call method cut_ by_ Zero, which splits the array by 0.

#The input array is naturally divided into sub arrays by 0
#For each subarray, check whether there is only one - 1, and if so, divide it
def cut_by_zero(nums):
    #No array of 0, check - 1
    if 0 not in nums:
        return cut_by_single_negative(nums)

    #Without any skill, traverse the array and temporarily store it in TMP
    ret_ls = []
    tmp = []
    for i in nums:
        if i == 0:
            #If you traverse to 0, check if there is a unique - 1 in TMP
            if len(tmp) != 0:
                ret_ls += cut_by_single_negative(tmp)
                tmp = []
        else:
            tmp.append(i)
    #Don't forget the tail
    if len(tmp) != 0:
        ret_ls += cut_by_single_negative(tmp)
    return ret_ls

Verify this method:

>>>cut_by_zero([1, 1, 1, 1, -1, 1, 0, 1])
[[1, 1, 1, 1], [1], [1]]

When implementing the traversal process in this method, the method of recording subscripts was used to replace the temporary list variable TMP to reduce the additional memory overhead. However, according to the submission results, the memory savings are limited, and the time overhead increases more. It is speculated that it is related to the list implementation mechanism of python, so the method of using temporary variables is reserved here.

3.is_ The positive method determines whether the product of a subarray that does not contain 0 is a positive number. Just judge whether the number of negative numbers is even.
def is_positive(sub_nums):
    negative_count = sub_nums.count(-1)
    if negative_count%2 == 0:
        return True
    else:
        return False

Verify it again

>>>print(is_positive([1, 1, 1, 1]))
>>>print(is_positive([-1, -1, 1, -1]))
True
False
4. Getmaxlenofsub finds the longest sub array that satisfies the condition in a sub array that does not contain 0, and enters the parameter sub_ Num is a sub array after segmentation. There is no 0 in it, and the number of – 1 is not 1. Return as soon as possible by judging some special situations first.
def getMaxLenofSub(sub_nums):
    len_sub = len(sub_nums)
    #If the product of the subarray itself is positive, the length of the array is returned
    if is_positive(sub_nums): 
        return len_sub
    #In special cases, when the length of the subarray is only 1, there must be only one 1
    if len(sub_nums) == 1:
        return 1
    #The longest subarray that meets the conditions is the length of the subarray
    #Start from the maximum length and decrease downward. As long as you find a sub array that meets the conditions, it is the longest sub array
    for l in range(len_sub-1, 0, -1):
        for index in range(len_sub-l+1):
            if is_positive(sub_nums[index:index+l]):
                return l
    return 1

Verify:

>>>print(getMaxLenofSub([1, 1, 1, 1]))
>>>print(getMaxLenofSub([-1, -1, 1, -1]))
4
3
5. The last is the overall process. First convert the numbers, then cut them into sub arrays, and then sort them according to the length of the sub arrays, so as to end the traversal process as soon as possible. Finally, traverse all sub arrays to find the qualified array length.
def getMaxLen(nums):
    #First replace the positive and negative integers with 1 and - 1
    nums = [format_nums(x) for x in nums]
    #Divide into subarrays by 0
    ls = cut_by_zero(nums)
    #Sort by the length of the subarray
    ls.sort(key=lambda x:len(x),reverse=True)
    
    #Record the length of the longest sub array that meets the conditions. The initial value is 0
    max_len_of_all = 0
    #Traverse all subarrays
    for sub_nums in ls:
        #If the length of the sub array traversed is less than max_ len_ of_ all
        #It means that it is impossible to get a longer qualified sub array
        #The array is sorted by length, so it can be concluded that max_ len_ of_ All is the qualified maximum value
        if len(sub_nums) < max_len_of_all:
            return max_len_of_all
        #Find the maximum length that meets the conditions from the subarray, and compare it with max_ len_ of_ All comparison
        sub_max = getMaxLenofSub(sub_nums)
        if sub_max > max_len_of_all:
            max_len_of_all = sub_max

    return max_len_of_all

verification

Several examples mentioned in the title:

>>>nums = [1,-2,-3,4]
>>>getMaxLen(nums)
4

>>>nums = [0,1,-2,-3,-4]
>>>getMaxLen(nums)
3

>>>nums = [-1,-2,-3,0,1] 
>>>getMaxLen(nums)
2

>>>nums = [-1,2]
>>>getMaxLen(nums)
1

>>>nums = [1,2,3,5,-6,4,0,10]
>>>getMaxLen(nums)
4

The last is the performance test case. For an array with a length of 100000, find the longest sub array length that meets the conditions:

%%time
nums = [1]*100000
nums[3000] = -1
getMaxLen(nums)

CPU times: user 14 ms, sys: 1.27 ms, total: 15.3 ms
Wall time: 15.1 ms

Submit

The results submitted to leetcode are shown in the figure below. The results submitted each time may fluctuate slightly.
Python solution leetcode no.1567


My Python version

>>> import sys
>>> print(sys.version)
3.7.6 (default, Jan  8 2020, 13:42:34) 
[Clang 4.0.1 (tags/RELEASE_401/final)]