每天两道LeetCodeHard:(50)

174 阅读3分钟

321. Create Maximum Number

拼接最大数

题干:

给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。

求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组

解释:

给了两个数组,选k个数字出来,按照原有顺序组成最大的数字 1,保持原序 2,k个字符

思考:

看到这种确定解空间的题目,第一反应就是应该是用dp来做,而且由于有两个数组+一个k,所以应该是三维的dp,那么接下来就是寻找递推关系了。

dp[i,j,k]=max( 10^(k-1)*num1[i]+dp[i-1,j,k-1], 选择使用第一个数组的数字 10^(k-1)*num2[j]+dp[i,j-1,k-1], 选择使用第二个数组的数字 dp[i-1,j-1,k] 两个数组都不选择 )

三维dp的初始化也比较麻烦,要初始化1个点,3个边和3个面,分别是i,j,k各自为0,和i,j,k两两为0,然后再根据上述式子迭代到最后,维护一个最大值,转化成数组返回即可。时间复杂度O(n3)

结果:非常缓慢,其实并不需要遍历所有的解空间,因为是有限的组合,而且只要找到数组1和数组2中最大的数,再拼起来就一定是总的最大的。 所以先找k个组合,O(n),对每个pair,找数组中对应的最大的数字组合,然后merge一下即可。 那么如何寻找固定长度的最大的数字组合呢?使用单调栈即可,限定出栈的次数,在出栈次数满之前保证栈中元素无降序对,这样就可以让大的数字尽可能的靠前站了。最后时间复杂度91%

答案:

参考了一个写的非常漂亮的python
class Solution(object):
    def maxNumber(self, nums1, nums2, k):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :type k: int
        :rtype: List[int]
        """
        def max_n_seq(arr,n):
            '''
            从array中选择长度为n的最大子数组
            '''
            if n<=0 or n >len(arr):
                return []
            if n==len(arr):
                return list(arr)
            popCount=len(arr)-n
            seq=[]
            for i in arr:
                while seq and i>seq[-1] and popCount>0 :
                    seq.pop()
                    popCount-=1
                seq.append(i)
            return seq[:n]
        def merge_2_seq(seq1, seq2):
            """合并2个序列组成值最大的序列"""
            max_seq = []
            while seq1 and seq2:
                max_seq.append(max(seq1, seq2).pop(0))
            return max_seq + (seq1 or seq2)
        ans = []
        for i in range(max(0, k - len(nums2)), 1 + min(len(nums1), k)):
            ans = max(ans, merge_2_seq(max_n_seq(nums1, i), max_n_seq(nums2, k - i)))
        return ans


答案补充:

这里有一个小tip,max(seq1,seq2)是可以直接按位比较两个不等长数组的,使用这个写法可以极大的缩短代码长度。 仅适用于python,不知道java能不能这么搞。