【问题标题】:Count the number of subarrays where the selected element is the largest value计算所选元素为最大值的子数组的数量
【发布时间】:2019-09-01 07:44:13
【问题描述】:

我们有一个整数数组X。任务是返回一个相同大小的数组Y,其中Y 中的ith 元素是X 中具有ith 元素的子数组的最大计数。

例如:

X: [8, 7, 1, 12, 11, 4, 10, 2, 3, 6, 9]
Y: [3, 2, 1, 32, 7, 1, 10, 1, 2, 3, 4]

这是我的二次时间复杂度解决方案。

def solve(A):

    def expand(i):
        left, right = i, i
        while left > 0 and A[i] >= A[left - 1]:
            left -= 1

        while right < len(A) - 1 and A[i] >= A[right + 1]:
            right += 1

        length = right - left + 1
        mid = i - left
        return (mid + 1) * (length - mid)

    result = [0] * len(A)
    MOD = 10**9 + 7
    for i in range(len(A)):
        count = expand(i)
        count %= MOD
        result[i] = count

    return result

我们的想法是,当元素大于当前元素时,我们使用两个指针向左和向右移动。一旦我们有了当前元素最大的数组,我们就可以通过(start_index) * (end_index - start_index + 1)得到子数组的数量

算法必须在非常大的测试用例上运行。如何将时间复杂度降低到至少NlogN

【问题讨论】:

  • X 值是否都不同?

标签: python algorithm math


【解决方案1】:

这是一个O(n) 版本。代码中的一条注释应该使这个想法很清楚。

JavaScript 代码:

// Preprocess previous higher and lower elements in O(n)
// Adapted from https://www.geeksforgeeks.org/next-greater-element
function prev(A, higherOrLower) {
  function compare(a, b){
    if (higherOrLower == 'higher')
      return a < b
    else if (higherOrLower == 'lower')
      return a > b
  }
  
  let result = new Array(A.length)
  let stack = [A.length - 1]

  for (let i=A.length-2; i>=0; i--){ 
    if (!stack.length){ 
      stack.push(A[i])
      continue
    }

    while (stack.length && compare(A[ stack[stack.length-1] ], A[i]))
      result[ stack.pop() ] = i

    stack.push(i)
  }

  while (stack.length)
    result[ stack.pop() ] = -1

  return result
}

function f(A){
  let prevHigher = prev(A, 'higher')
  let nextHigher = prev(
    A.slice().reverse(), 'higher')
    .map(x => A.length - x - 1)
    .reverse()
  let result = new Array(A.length)
  
  for (let i=0; i<A.length; i++)
    result[i] =
      (i - prevHigher[i]) * (nextHigher[i] - i)
              
  return result
}

var A = [8, 7, 1, 12, 11, 4, 10, 2, 3, 6, 9]

console.log(JSON.stringify(A))
console.log(JSON.stringify(f(A)))

【讨论】:

    【解决方案2】:

    为最大值构建一个分段树。然后,对于每个元素,您将能够对第一个元素进行二进制搜索,即向右和向左更大。这些较大元素之间的子数组是最大子数组,其中所选元素是最大的。时间复杂度为 O(N log2 N)

    【讨论】:

      【解决方案3】:

      考虑到整数都是不同的,可以根据它们的等级对整数索引进行排序。

      从较低的元素开始,我们知道它们的值应该是 1。

      然后我们提供一个链接区间数组:如果一个更大的元素周围没有链接区间,则它的值为 1(因为下一个更大的元素围绕它)。

      如果我们有链接的区间,我们会将它们考虑到子数组计数中,并更新结束(前一个区间)和开始(下一个区间),以便全局区间围绕新考虑的整数链接。

      # Class to track of already used intervals
      class Link:
          def __init__( self, start, end ):
              self.start = start
              self.end = end
      
          def __str__( self ):
               return str( self.start ) + "/" + str( self.end )
      
      # Input
      X = [8, 7, 1, 12, 11, 4, 10, 2, 3, 6, 9]
      
      l = len(X)
      
      # Prepare the output
      Y = [0] * l
      
      # Prepare link table
      links = [None] * l
      
      # Map the integers sorted ranks
      for i in sorted( range(l), key = lambda i: X[i] ):
          # See if links are availables around current index
          link_previous = links[i - 1] if i > 0 else None
          link_next = links[i + 1] if i < (l - 1) else None
      
          # Calculate start/end positions
          start = link_previous.start if link_previous else i
          end = link_next.end if link_next else i
      
          # Define the current link interval
          links[i] = Link( start, end )
      
          # Report end at the previous linked interval
          if link_previous:
              links[link_previous.start].end = end
      
          # Report start at the next linked interval
          if link_next:
              links[link_next.end].start = start
      
          # Calculate subarray count
          Y[i] = (1 + i - start) * (1 + end - i)
      
          print( [str(l) for l in links] )
          print( "Y", Y )
      
      
      print( "Y [3, 2, 1, 32, 7, 1, 10, 1, 2, 3, 4]" )
      

      对于排序应该是 nLog(n),然后是 n。

      注意:如果整数并非完全不同,我认为添加另一个开始/结束(+对应值,以便我们拥有当前和整体间隔)应该可以工作。但我不知道在这种情况下子数组计数是什么。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-11-02
        • 1970-01-01
        • 1970-01-01
        • 2017-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-19
        相关资源
        最近更新 更多