0

Leetcode question: Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1.

Leetcode entered a very long input of to the function and its IDE couldn't handle the inflated memory use

(update: added 2 better versions)

In the first versions I appended all the possible subarrys to a 2d list. I removed that in these version. Now I have treated every list once i create it.

Leetcode still claims time exceeded

this is part their input (it's double or triple this length)

[1,1,0,1,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,1,0,1,1,1,0,0,0,1,1,0,0,1,1,0,1,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,1,1,1,1,0,1,1,0,0,0,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,1,1,0,1,0,0,1,0,1,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,1,0,1,1,1,1,1,1,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,0,0,1,1,1,1,

Appreciate help with optimizing these:

class Solution(object):
    
    
    #version 2
    
   def findMaxLength2( nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    l_sub = []   #list of subarray derived from nums
    LEN = len(nums)
    _max  = 0
    len_max = 0

    # divides into subarrays
    for i in range(LEN):
        count = [0] * 2
        for j in range(i + 1, LEN + 1):
            _str = "".join([str(_m) for _m  in nums[i:j]])
            count[0]=  _str.count("0")
            count[1] =  _str.count ("1")
            if count[0] == count[1] and count[1] >= _max :
                _max  = count[0]
                len_max = _max  * 2
    return len_max

#version 3:

def findMaxLength3( nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    l_sub = []   #list of subarray derived from nums
    LEN = len(nums)
    _max  = 0
    len_max = 0

    # divides into subarrays
    for i in range(LEN):
        for j in range(i + 1, LEN + 1):
            ones = sum(filter(lambda x:x ==1 , nums[i:j]))
            zeros= len (nums[i:j]) - ones
            if ones == zeros and zeros >= _max :
                _max  = zeros
                len_max = _max  * 2
            
    return len_max



                    
1
  • And for gosh sakes don't convert to strings! .count works on lists of integers, as nums[i:j].count(0). Better than that is sum(nums[i:j]) - (j-i), but even better is the incremental scheme described by Cincinnatus. Commented Nov 16, 2024 at 1:28

1 Answer 1

3

Let pre[i] represent the number of elements with value 1 in the prefix of the array up to index i. For a subarray to have an equal number of zeros and ones, it is clear that the number of ones is equal to half the length of the subarray (which implies the subarray length is even). Consequently, we are looking for two indexes l < r such that pre[r] - pre[l] = (r - l) / 2. Rearranging, we get 2 * pre[r] - r = 2 * pre[l] - l. Ergo, we can compute 2 * pre[i] - i for each index and search for the lowest previous index that has a matching value to determine the bounds of a longest valid subarray at each index.

class Solution:
    def findMaxLength(self, nums: list[int]) -> int:
        min_idx = {}
        max_len = curr = 0
        for i, v in enumerate(nums):
            min_idx.setdefault(2 * curr - (i - 1), i - 1)
            curr += v
            max_len = max(max_len, i - min_idx.get(2 * curr - i, i))
        return max_len

Alternatively, we may assign the values 1 and -1 to 1 and 0, so that they cancel out to 0. The problem is then transformed to finding the longest subarray with a sum of 0. We may again employ a prefix sum array, noting that the sum of a subarray from index i to index j is the sum of all elements up to index i - 1 subtracted from the sum of elements up to index j, i.e. pre[j] - pre[i - 1].

class Solution:
    def findMaxLength(self, nums: list[int]) -> int:
        min_idx = {}
        max_len = curr = 0
        for i, v in enumerate(nums):
            min_idx.setdefault(curr, i - 1)
            curr += 2 * v - 1
            max_len = max(max_len, i - min_idx.get(curr, i))
        return max_len

Both of these solutions have have linear (expected) time complexity and linear space complexity.

4
  • 1
    Short version with or for the balance update and without extra get.
    – no comment
    Commented Nov 16, 2024 at 13:57
  • 1
    And linear time worst case version.
    – no comment
    Commented Nov 16, 2024 at 14:16
  • @nocomment That's a very nice golf! Good point about using a list instead of a dict as well. Commented Nov 16, 2024 at 20:30
  • Btw I suspect CPython's dict makes our dict solutions worst case linear as well. Since our keys are a full int range (no gaps).
    – no comment
    Commented Nov 16, 2024 at 20:41

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.