首先,构建一个有序映射表,记录 A 中不同元素的计数。
然后,通过数组索引向前迭代(从 0 到 n-1),从该映射表中“撤回”元素。在每个点上,有三种情况:
1. 如果 i < n-1,并且可以选择 A[i] == B[i],则这么做并继续向前迭代。
2. 否则,如果可以选择 A[i] < B[i],则选择最大的可选值 A[i] < B[i]。然后,选择所有后续数组指数的最大可用值。(此时您无需再担心维护 A[i] <= B[i],因为我们已经在 A[i] < B[i] 的索引之后了)。返回结果。
3. 否则,我们需要回溯到上次可以选择 A[i] < B[i] 的索引处,然后使用上一个项目中的方法。
请注意,尽管需要回溯,但此处的最坏情况是三次遍历:一次前向遍历使用第一个项目中的逻辑,一次后向遍历回溯到找到最后一个可以选择 A[i] < B[i] 的索引处,然后使用第二个项目中的逻辑进行最终前向遍历。
由于维护有序映射表的开销,这需要 O(n log m) 时间和 O(m) 额外空间,其中 n 是 A 的总元素数,m 是不同元素的数量。(由于 m ≤ n,我们也可以将其表达为 O(n log n) 时间和 O(n) 额外空间。)
请注意,如果没有解决方案,则回溯步骤将一直到达 i == -1。如果发生这种情况,您可能需要引发异常。
编辑后添加(2019-02-01):
现在已删除的答案中,גלעד ברקן总结了这个目标:
要使字典顺序更小,该数组必须具有从左到右的初始可选部分,其中 A[i] = B[i],并以元素 A[j] < B[j] 结束。为了最接近 B,我们希望最大化该部分长度,然后最大化数组的剩余部分。
因此,考虑到这个总结,另一种方法是进行两个独立的循环,第一个循环确定初始段的长度,第二个循环实际填充
A
。这相当于上面的方法,但可能会使代码更清晰。所以:
- 构建一个有序映射,记录
A
中不同元素的计数。
- 初始化
initial_section_length := -1
。
- 遍历数组索引0到n−1,“撤回”这个映射中的元素。对于每个索引:
- 如果可以选择一个尚未使用过的
A
元素,该元素小于当前B
元素,则将initial_section_length
设置为当前数组索引。(否则,不执行此操作。)
- 如果无法选择一个尚未使用过的
A
元素,该元素等于当前B
元素,则退出此循环。(否则,继续循环。)
- 如果
initial_section_length == -1
,则没有解决方案;抛出异常。
- 重复步骤#1:重新构建有序映射。
- 遍历数组索引从0到
initial_section_length-1
,“撤回”映射中的元素。对于每个索引,选择一个尚未使用过的A
元素,该元素等于当前B
元素。(第一个循环确保了这样的元素的存在。)
- 对于数组索引
initial_section_length
,选择最大的尚未使用的A
元素,该元素小于当前B
元素(并从映射中“撤回”它)。 (第一个循环确保了这样的元素的存在。)
- 遍历数组索引从
initial_section_length+1
到n−1,继续从映射中“撤回”元素。对于每个索引,选择尚未使用的A
元素中的最大元素。
此方法具有与基于回溯的方法相同的时间和空间复杂度。
C
,使得对其使用next_permutation
函数可以得到B
? - user58697