Codewars任务Simple Fun #159: Middle Permutation执行超时求助
解决Codewars Simple Fun #159: Middle Permutation的超时问题
我完全懂你的困扰——暴力生成所有排列再取中间项的思路,在字符串长度稍大时(比如n≥10)会直接因为阶乘级的计算量超时,毕竟10!就有360多万个排列,更别说更长的字符串了。咱们换个思路:直接构造中间排列,不需要生成所有排列,这样时间复杂度能降到O(n²),完全能在12000ms内跑过所有测试用例。
核心思路
要构造中间排列,我们可以利用阶乘的特性:对于已排序的字符数组,每个字符作为开头时,后面的字符能生成(n-1)!个排列。我们可以通过计算中间位置对应的每一位字符,逐步确定最终的排列。
具体步骤如下:
- 先排序字符:把输入字符串的字符按字典序升序排列,这是后续计算的基础。
- 预计算阶乘:提前算出从0!到(n-1)!的阶乘值(n是字符串长度),方便后续每一步的索引计算。
- 确定中间位置:排列总数是
n!,题目要求:- 当n!为偶数时,中间项是第
n!/2项(1-based),对应0-based索引为(n!//2) - 1; - 当n=1时,唯一的排列就是结果。
- 当n!为偶数时,中间项是第
- 逐位构造排列:
- 对于当前剩余的字符,计算每个字符开头对应的排列数(即
(剩余字符数-1)!); - 用当前剩余位置除以这个排列数,得到要选的字符在剩余列表中的索引;
- 把选中的字符加入结果,从剩余列表中移除该字符;
- 更新剩余位置为当前位置对排列数取余;
- 重复直到所有字符都被选中。
- 对于当前剩余的字符,计算每个字符开头对应的排列数(即
示例实现(Python)
import math def middle_permutation(s): chars = sorted(s) n = len(chars) if n == 1: return s # 预计算阶乘数组,fact[i] 表示 i! fact = [1] * n for i in range(1, n): fact[i] = fact[i-1] * i # 计算0-based的中间位置 mid_position = fact[n] // 2 - 1 result = [] remaining_chars = chars.copy() for i in range(n, 0, -1): group_size = fact[i-1] # 确定当前要选的字符索引 char_index = mid_position // group_size result.append(remaining_chars.pop(char_index)) # 更新剩余位置 mid_position = mid_position % group_size return ''.join(result)
为什么这个方法高效?
- 时间复杂度:主要开销在排序(O(n log n))和逐位构造(O(n²),因为每次从列表中移除元素是O(n)操作),对于n=20来说,20²=400次操作,和20!的天文数字比完全不在一个量级。
- 空间复杂度:只需要存储剩余字符和结果,是O(n)级别的,非常节省内存。
样例验证
比如输入"abc":
- 排序后为
['a','b','c'],n=3,阶乘数组为[1,1,2,6],中间位置是6//2 -1=2; - 第一次循环:每组排列数是2! =2,选中索引1的
'b',剩余['a','c'],剩余位置更新为2%2=0; - 第二次循环:每组排列数是1! =1,选中索引0的
'a',剩余['c']; - 最后加入
'c',结果为"bac",和题目要求的中间项一致(6个排列的第3项,1-based)。
这个方法完全避免了生成所有排列,不管n多大(只要阶乘在Python整数范围内),都能快速计算出结果,肯定能通过所有测试用例。
内容的提问来源于stack exchange,提问作者Dennis Braun
相关产品推荐
相关产品推荐

