字典顺序最小的神奇排列 | 豆包MarsCode AI 刷题

77 阅读2分钟

问题描述

小R遇到了一个非常有趣的数学问题。他手里有一个排列A,这个排列是从1到N的整数序列。我们称排列A为神奇排列,当且仅当该排列能够生成恰好K个不同的整数。

对于排列A,如果存在某个位置i,使得A[i] + i等于一个整数X,那么我们说整数X是由排列A生成的。

例如,当K为2时,排列[2, 1, 3]是神奇的,因为通过计算A[i] + i,生成了两个不同的整数:25

现在,你得到两个整数NK,请你返回从1到N的整数的字典顺序最小的神奇排列P。可以证明在给定约束下,总是存在至少一个符合要求的神奇排列。

测试样例

样例1:

输入:n = 4, k = 3 输出:[1, 2, 4, 3]

样例2:

输入:n = 5, k = 2 输出:[1, 5, 4, 3, 2]

样例3:

输入:n = 6, k = 4 输出:[1, 2, 3, 6, 5, 4]

要求

  • 返回的排列必须是字典顺序最小的。
  • 排列A必须能够生成恰好K个不同的整数。
  • 排列A是从1到N的整数序列。

程序思路概述与关键点详细解析

1. 特殊情况处理

  • ( N == K )

    • 如果需要生成的不同整数数量等于排列的大小 ( N ),每个位置 ( A[i] + i ) 都能生成唯一的整数,无需调整。
    • 直接返回: [1, 2, ..., N]
  • ( K == 1 )

    • 如果只需要生成 1 个整数,所有元素反转(如 [1, 2, 3] -> [3, 2, 1]),保证 A[i] + i 的结果相同。

2. 遍历调整子区间

  • 枚举从索引 i = 1 开始的子区间:

    • res = A[:] 创建当前排列的副本。
    • res[i:] = reversed(res[i:]) 反转子区间,尝试生成新的排列。

3. 检查生成的不同整数

  • 对当前排列 res,计算所有可能的 A[i] + i 的值:

    • 使用 set 去重,统计生成的不同整数数量。

4. 字典序最小性

  • 枚举从索引 i = 1 开始的反转操作,保证尽可能少地改变排列结构,从而保持字典序最小。
def solution(n: int, k: int) -> list:
    A = list(range(1, n + 1))
    if n == k:
        return A
    if k == 1:
        return A[::-1]
​
    for i in range(1, n):
        res = A[:]
        res[i:] = reversed(res[i:])
        tem = set([j + v for j, v in enumerate(res)])
        if len(tem) == k:
            return res
​
​
if __name__ == '__main__':
    print(solution(n=4, k=3) == [1, 2, 4, 3])
    print(solution(n=5, k=2) == [1, 5, 4, 3, 2])
    print(solution(n=6, k=4) == [1, 2, 3, 6, 5, 4])
    print(solution(n=1, k=1) == [1])
    print(solution(n=14, k=1) == list(reversed(range(1, 14 + 1))))
​

程序复杂度分析

  1. 时间复杂度

    • 枚举子区间反转:最多 ( N-1 ) 次。
    • 每次计算不同整数数量(使用集合):最多 ( O(N) )。
    • 总复杂度:( O(N^2) )。
  2. 空间复杂度

    • restem:额外空间为 ( O(N) )。
    • 总复杂度:( O(N) )。