LCP 23-魔术排列
秋日市集上,魔术师邀请小扣与他互动。魔术师的道具为分别写有数字 1~N
的 N
张卡牌,然后请小扣思考一个 N
张卡牌的排列 target
。
魔术师的目标是找到一个数字 k(k >= 1),使得初始排列顺序为 1~N
的卡牌经过特殊的洗牌方式最终变成小扣所想的排列target
,特殊的洗牌方式为: - 第一步,魔术师将当前位于 偶数位置 的卡牌(下标自 1 开始),保持 当前排列顺序 放在位于
奇数位置 的卡牌之前。例如:将当前排列 [1,2,3,4,5] 位于偶数位置的 [2,4] 置于奇数位置的 [1,3,5] 前,排列变为
[2,4,1,3,5]; - 第二步,若当前卡牌数量小于等于 k
,则魔术师按排列顺序取走全部卡牌;若当前卡牌数量大于 k
,则取走前 k
张卡牌,剩余卡牌继续重复这两个步骤,直至所有卡牌全部被取走; 卡牌按照魔术师取走顺序构成的新排列为「魔术取数排列」,请返回是否存在这个数字 k
使得「魔术取数排列」恰好就是 target
,从而让小扣感到大吃一惊。 示例 1: >输入:target = [2,4,3,1,5]
>
输出:
true
> >解释:排列 target 长度为 5,初始排列为:1,2,3,4,5。我们选择 k = 2: >第一次:将当前排列
[1,2,3,4,5] 位于偶数位置的 [2,4] 置于奇数位置的 [1,3,5] 前,排列变为 [2,4,1,3,5]。取走前 2 张卡牌 2,4,剩余
[1,3,5]; >第二次:将当前排列 [1,3,5] 位于偶数位置的 [3] 置于奇数位置的 [1,5] 前,排列变为 [3,1,5]。取走前 2 张
3,1,剩余 [5]; >第三次:当前排列为 [5],全部取出。 >最后,数字按照取出顺序构成的「魔术取数排列」2,4,3,1,5 恰好为 target。
示例 2: >输入:target = [5,4,3,2,1]
> >输出:false
> >解释:无法找到一个数字 k
可以使「魔术取数排列」恰好为 target。 提示: -1 <= target.length = N <= 5000
- 题目保证target
是1~N
的一个排列。
比赛的时候思路出来了,可惜具体实现出了点问题,一直超时。
思路:
1、暴力法(超时)
题目给出了样例:
target = [2,4,3,1,5]
根据题目规则的第二步:
1 | 第一步, |
我们显然可以使用暴力法,**k
的有效取值是从1到n**,当k大于n时,得到的效果其实和k==n
是一样的。
所以我们直接把所有k的有效取值都按照题目的规则(洗牌、取牌)试一遍,肯定能找到答案。
但是这样显而易见超时了。
2、改进:充分利用第一次洗牌结果
超时之后我们就要思考如何优化k的取值。
这里依旧使用题目所给的target数组:
target:{2,4,3,1,5\
1、构造一个数组:
{1,2,3,4,5}
2、按照题目的规则排序,即把索引(从1开始)为偶数的放前面,得到第一次洗牌的结果(叫他firstSort
数组吧):
firstSort数组:{2,4,1,3,5\
3、然后我们把题目给的目标数组target从头开始进行比较。
firstSort数组:{\textcolor{red}{2,4}, 1,3,5
target数组: {\textcolor{red}{2,4},3,1,5\
我们可以看到标红的2,4
是两个数组最长公共前缀,设最长公共前缀的长度为Len
。
这也就是说两个数组的前Len
个数相同,第Len+1
个数肯定不同。(除非两个数组完全相同,那就没有第Len+1
个数了)
我们可以发现,k的取值是不能超过Len的:
因为如果k
超过了Len
,你第一次洗牌后取前k
个数,其中前Len
个数相同,第Len+1
个数不同,那得到的结果肯定不同。
比如说在这个例子中,Len
是等于2的,我取k
等于3。那么第一次取出的k个数为:
{2,4,\textcolor{red}1
而target的前k
个数为:
{2,4,\textcolor{red}3
很明显,后面不用继续取数,我们也知道结果不可能与target一样了。
那么k
能不能小于Len
呢?
答案是不能的。
我们简单地分析一下:
如图是第一次洗牌后的情况与target做对比。
先假设 k=Len-1
的情况,以简要分析。如图我们取出前Len-1
个数,那么第Len个数就肯定被保留下来,参与下一轮洗牌。
接下来,进行第二轮洗牌,如图所示,firstSort数组中的第一个数肯定会因为洗牌与target中相同的数错开(除非就剩一张牌,但是在第二轮洗牌中不可能只剩一张,因为这两个数组不可能前n-1个相同而最后一个数不同)。
因此,两个数组中的第一个数肯定不同。
并且,我们可以推广到k取Len-3 , Len-5 , Len-7 …
一直到Len–num
,其中num
为表示任意奇数。
只要num为奇数,那么在新数组中的第1个数在洗牌后必然跟在target
中的与他相同的数错开(看图容易理解)。
如果num
为偶数的话,例如num=2,即k=Len-2
,分析如下:
经过上图简单的分析,显然num为2的时候剩余数组的第2个又会因为洗牌而错开,导致与结果不一致。
同样的道理可以推广到num=4,num=6...
直到num
为任意偶数。
所以到最后,我们发现只要对k==Len
的情况进行模拟洗牌、取牌的过程,判断最后的结果是否与target相同就行。当然,如果中途发现不对,就可以提前退出
1 | class Solution { |
1 | class Solution { |
代码写的难看了,没做过多优化,希望评论区大佬有更好的思路、写法补充。
码字不易,点个赞吧