【问题标题】:alternating substring using (recursive) dynamic programming使用(递归)动态编程的交替子串
【发布时间】:2019-12-18 00:56:18
【问题描述】:

我的任务是用递归动态规划方法解决交替子字符串问题:

考虑一个整数序列 A = a1, a2, a3, ... an。 a 的子序列 B 是一个序列 B = b1, b2, .... ,bn,它是通过删除一些元素但保持顺序从 A 创建的。给定一个整数序列 A,目标是计算 anb 交替子序列 B,即序列 b1, ... bn 使得对于 {2, 3, ... , m-1} 中的所有 i,如果 b{i- 1} b{i+1} 如果 b{i-1} > b{i} 然后 b{i}

到目前为止,我需要检查每个递归步骤,如果我想获取元素并寻找下一个交替数字,或者我只是获取下一个数字并从两种可能的交替开始。

s 从左开始的索引 e结束(len(数组)) 一个数组 g(A,s) 获得下一个更大或更小的整数的函数。

我的递归公式是: V(A, s, e) = max( V(A, g(A,s),e), V(A, s+1, e) ) +1

V(A, g(A,s),e) 获取元素并继续下一个交替的元素

V(A, s+1, e) 离开元素并在下一个元素开始新的序列

假设我的实现和方法是正确的,我建议运行时间为 O(n^2),因为我们需要知道每个组合。

如果没有催眠部分,它将是 O(2^n),就像二叉树的叶子数量一样。

这个分析正确吗?可能只对公式正确,但对代码不正确...

函数getsmaller和getbigger是g(A,s)

A = [5,6,5,5,5,7,5,5,5,87,5]

s = 0
e = len(A)

memo_want_small = [-1] * len(A)
memo_want_bigger = [-1] * len(A)


def getsmaller(A, s):
    erg = 0
    for i in range(s, len(A)):
        if A[i] < A[s]:
            if i is not None:
                 return i   
    return -1

def getbigger(A, s):
    for i in range(s, len(A)):
        if A[i] > A[s]:
            if i is not None:
                return i      
    return -1


def wantsmall(A, s, e):
    if s == -1: # no more alternating element
        return 0
    if s == e: # base case
        return 0
    if memo_want_small[s] is not -1:
        return memo_want_small[s]

    return_V = max(wantbigger(A, getbigger(A, s), e)  , alt(A, s+1, e)) + 1
    memo_want_small[s] = return_V
    return return_V

def wantbigger(A, s, e):
    if s == -1: # no more alternating element
        return 0
    if s == e: # base case
        return 0
    if memo_want_bigger[s] is not -1:
        return memo_want_bigger[s]

    return_V = max(wantsmall(A, getsmaller(A, s), e)  , alt(A, s+1, e)) + 1
    memo_want_bigger[s] = return_V
    return return_V

def alt(A, s, e):
    if s == e: # base case
        return 0
    return max(wantsmall(A, getsmaller(A, s), e), wantbigger(A, getbigger(A, s), e)) 



print("solution: " + str(alt(A,s,e)))

【问题讨论】:

  • 问题和任务描述不清楚。
  • 添加问题集

标签: algorithm runtime dynamic-programming topdown


【解决方案1】:

让我们考虑一个从A[i] 向左的序列,方向首先向上。

首先,不可能有更高的元素,A[j]A[i] 的左边结束一个更长的序列,因为如果有,我们总是可以用A[i] 切换那个元素并以一个向上结束- 相同长度的第一个序列。

* Going left from A[i], up-first

     ↖
       A[j]
            ...  A[i]

其次,左边不可能有一个较低的元素A[j],结束一个较长的先上序列,而中间的一个元素A[k]高于A[i],因为这样我们就可以将A[i] 和更高的元素相加,得到一个更长的序列。

* Going left from A[i], up-first

                A[k]
            ...      ...  A[i]
     ↖
       A[j]

因此,向左看,以A[i] 结尾的最长的先上序列要么(1)长度相同或长于以左侧下一个更高元素结尾的序列,要么(2)长度相同序列结束于任何连续的、单调递增的子数组的最低元素,到达A[i]

现在,考虑一个元素A[r],它位于A[i] 右侧的第一个元素,我们希望找到以它结尾的最长的向下优先序列。正如我们所展示的,在计算A[i] 的结果时,已经可以考虑到A[i] 左侧的元素,这些元素以向上优先序列结束并且高于或低于A[i],因此它仍然是唯一的用于计算以A[r] 结尾的最长向下优先序列的感兴趣单元格(向左看)。这指向一个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 longestAlternatingSequence(A){
  let prevHigher = prev(A, 'higher')
  let prevLower = prev(A, 'lower')
  let longestUpFirst = new Array(A.length)
  let longestDownFirst = new Array(A.length)
  let best = 1
  
  longestUpFirst[0] = 1
  longestDownFirst[0] = 1
  
  for (let i=1; i<A.length; i++){
    // Longest up-first
    longestUpFirst[i] = Math.max(
      A[i] >= A[i-1] ? longestUpFirst[i - 1] : -Infinity,
      prevHigher[i] != -1 ? longestUpFirst[ prevHigher[i] ] : -Infinity,
      prevHigher[i] != -1 ? 1 + longestDownFirst[ prevHigher[i] ] : -Infinity,
      1
    )
    
    // Longest down-first
    longestDownFirst[i] = Math.max(
      A[i] <= A[i-1] ? longestDownFirst[i - 1] : -Infinity,
      prevLower[i] != -1 ? longestDownFirst[ prevLower[i] ] : -Infinity,
      prevLower[i] != -1 ? 1 + longestUpFirst[ prevLower[i] ] : -Infinity,
      1
    )

    best = Math.max(best, longestUpFirst[i], longestDownFirst[i])
  }

  console.log(`${ longestUpFirst } (longestUpFirst)`)
  console.log(`${ longestDownFirst } (longestDownFirst)`)
  
  return best
}

var tests = [
  [5,6,5,5,5,7,5,5,5,87,5],
  [1,2,3,4,5,6,7,8],
  new Array(10).fill(null).map(_ => ~~(Math.random()*50))
]

for (let A of tests){
  console.log(JSON.stringify(A))
  console.log(longestAlternatingSequence(A))
  console.log('')
}

更新

嘿,这里有一个更简单的 O(n) 递归:https://www.geeksforgeeks.org/longest-alternating-subsequence/

【讨论】:

    猜你喜欢
    • 2020-05-19
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 2020-07-24
    • 2020-12-10
    • 1970-01-01
    相关资源
    最近更新 更多