In combinatorial mathematics, a derangement is a permutation of the elements of a set, such that no element appears in its original position.

There's originally an array consisting of n integers from 1 to n in ascending order, you need to find the number of derangement it can generate.

Also, since the answer may be very large, you should return the output mod 109 + 7.

Example 1:

Input: 3
Output: 2
Explanation: The original array is [1,2,3]. The two derangements are [2,3,1] and [3,1,2].

Note:
n is in the range of [1, 106].

这道题给了我们一个数组,让我们求其错排的个数,所谓错排就是1到n中的每个数字都不在其原有的位置上,全部打乱了,问能有多少种错排的方式。博主注意到了这道题又让对一个很大的数取余,而且每次那个很大的数都是109 + 7,为啥大家都偏爱这个数呢,有啥特别之处吗?根据博主之前的经验,这种结果很大很大的题十有八九都是用dp来做的,那么就建一个一维的dp数组吧,其中dp[i]表示1到i中的错位排列的个数。那么难点就是找递推公式啦,先从最简单的情况来看:

n = 1 时有 0 种错排

n = 2 时有 1 种错排 [2, 1]

n = 3 时有 2 种错排 [3, 1, 2], [2, 3, 1]

然后博主就在想知道了dp[2],能求出dp[3]吗,又在考虑是不是算加入数字3的情况的个数。结果左看右看发现没有啥特别的规律,又在想是不是有啥隐含的信息需要挖掘,还是没想出来。于是看了一眼标签,发现是Math,我的天,难道又是小学奥数的题?挣扎了半天最后还是放弃了,上网去搜大神们的解法。其实这道题是组合数学种的错排问题,是有专门的递归公式的。

我们来想n = 4时该怎么求,我们假设把4排在了第k位,这里我们就让k = 3吧,那么我们就把4放到了3的位置,变成了:

x x 4 x

我们看被4占了位置的3,应该放到哪里,这里分两种情况,如果3放到了4的位置,那么有:

x x 4 3

那么此时4和3的位置都确定了,实际上只用排1和2了,那么就相当于只排1和2,就是dp[2]的值,是已知的。那么再来看第二种情况,3不在4的位置,那么此时我们把4去掉的话,就又变成了:

x x x

这里3不能放在第3个x的位置,在去掉4之前,这里是移动4之前的4的位置,那么实际上这又变成了排1,2,3的情况了,就是dp[3]的值。

再回到最开始我们选k的时候,我们当时选了k = 3,其实k可以等于1,2,3,也就是有三种情况,所以dp[4] = 3 * (dp[3] + dp[2])。

那么递推公式也就出来了:

dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2])

有了递推公式,代码就不难写了吧,参见代码如下:

解法一:

class Solution {
public:
int findDerangement(int n) {
if (n < ) return ;
vector<long long> dp(n + , );
dp[] = ; dp[] = ;
for (int i = ; i <= n; ++i) {
dp[i] = (dp[i - ] + dp[i - ]) * (i - ) % ;
}
return dp[n];
}
};

下面这种解法精简了空间,因为当前值只跟前两个值有关系,所以没必要保留整个数组,只用两个变量来记录前两个值,并每次更新一下就好了,参见代码如下:

解法二:

class Solution {
public:
int findDerangement(int n) {
long long a = , b = , res = ;
for (int i = ; i <= n; ++i) {
res = (i - ) * (a + b) % ;
a = b;
b = res;
}
return (n == ) ? : res;
}
};

下面这种方法是对之前的递推公式进行了推导变形,使其只跟前一个数有关,具体的推导步骤是这样的:

我们假设 e[i] = dp[i] - i * dp[i - 1]

递推公式为:  dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2])

将递推公式带入假设,得到:

e[i] = -dp[i - 1] + (n - 1) * dp[i - 2] = -e[i - 1]

从而得到 e[i] = (-1)^n

那么带回假设公式,可得: dp[i] = i * dp[i - 1] + (-1)^n

根据这个新的递推公式,可以写出代码如下:

解法三:

class Solution {
public:
int findDerangement(int n) {
long long res = ;
for (int i = ; i <= n; ++i) {
res = (i * res + (i % == ? : -)) % ;
}
return res;
}
};

参考资料:

https://discuss.leetcode.com/topic/94429/o-n-short-java-code

https://discuss.leetcode.com/topic/94767/java-dp-solution-with-explanation

https://discuss.leetcode.com/topic/94421/java-solution-with-explanation-by-using-staggered-formula

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Find the Derangement of An Array 找数组的错排的更多相关文章

  1. [LeetCode] Find All Duplicates in an Array 找出数组中所有重复项

    Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others ...

  2. [LeetCode] Single Element in a Sorted Array 有序数组中的单独元素

    Given a sorted array consisting of only integers where every element appears twice except for one el ...

  3. [LeetCode] Peak Index in a Mountain Array 山形数组的顶峰坐标

    Let's call an array A a mountain if the following properties hold: A.length >= 3 There exists som ...

  4. LeetCode 977. Squares of a Sorted Array (有序数组的平方)

    题目标签:Array 题目给了我们一组 从小到大的 integers,让我们平方数字 并且 也排序成 从小到达. 因为有负数在里面,平方后,负数在array的位置会变动. 可以设left 和 righ ...

  5. [LeetCode] 26. Remove Duplicates from Sorted Array 有序数组中去除重复项

    Given a sorted array nums, remove the duplicates in-place such that each element appear only once an ...

  6. leetCode 26.Remove Duplicates from Sorted Array(删除数组反复点) 解题思路和方法

    Remove Duplicates from Sorted Array Given a sorted array, remove the duplicates in place such that e ...

  7. [leetcode]26. Remove Duplicates from Sorted Array有序数组去重(单个元素只出现一次)

    Given a sorted array nums, remove the duplicates in-place such that each element appear only once an ...

  8. [LeetCode] 80. Remove Duplicates from Sorted Array II 有序数组中去除重复项 II

    Given a sorted array nums, remove the duplicates in-place such that duplicates appeared at most twic ...

  9. [leetcode]81. Search in Rotated Sorted Array II旋转过有序数组里找目标值II(有重)

    This is a follow up problem to Search in Rotated Sorted Array, where nums may contain duplicates. 思路 ...

随机推荐

  1. CentOS7搭建solr7.2

    solr介绍 一.Solr它是一种开放源码的.基于 Lucene Java 的搜索服务器,易于加入到 Web 应用程序中. 二.Solr 提供了层面搜索(就是统计).命中醒目显示并且支持多种输出格式( ...

  2. lua 二维数组创建

    local arr= {} for i=1, 4 do arr[i] = {} end 使用时可以直接使用arr[i][j]

  3. React demo:express、react-redux、react-router、react-roter-redux、redux-thunk(一)

    近期终于把之前留下的坑填上了(说了好久的要网站重写,总算是写完了),不过最后的在线添加文章,功能虽然做了,后台没把接口加上,实在是没精力去折腾了,公司又有事要忙,现在把从0开始到完成的一个思路来写一下 ...

  4. [BZOJ 1040][ZJOI2008]骑士

    1040: [ZJOI2008]骑士 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5403  Solved: 2060[Submit][Status ...

  5. hibernate框架学习笔记3:API详解

    Configuration对象: package api; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configur ...

  6. alpha-咸鱼冲刺day4

    一,合照 emmmmm.自然还是没有的. 二,项目燃尽图 三,项目进展 QAQ具体工作量没啥进展.但是前后端终于可以数据交互了!.. 四,问题困难 日常啥都不会,百度真心玩一年. 还得自学nodejs ...

  7. 简单的C语言编译器--语法分析器

      语法分析算是最难的一部分了.总而言之,语法分析就是先设计一系列语法,然后再用设计好的语法去归约词法分析中的结果.最后将归约过程打印出来,或者生成抽象语法树. 1. 设计文法 以下是我的文法(引入的 ...

  8. Python choice() 函数

    Python choice() 函数  Python 数字 描述 choice() 方法返回一个列表,元组或字符串的随机项. 语法 以下是 choice() 方法的语法: import random ...

  9. Raid5两块硬盘掉线可以恢复数据吗_raid数据恢复案例分享

    本案例中发生故障的存储类型是HP P2000,虚拟化平台为vmware exsi,共有10块硬盘组成raid5(硬盘容量为1t,其中6号盘是热备盘),由于某些故障导致阵列中两块硬盘亮黄灯掉线,硬盘无法 ...

  10. var 和 let 的异同?

    相同点 声明后未赋值表现一致 不同点 1.使用未声明的变量表现不同 2.变量作用范围不同 3.var可以声明多次 let只能声明一次 let的好处就是当我们在写代码的时候可以避免在不知道的情况下重复声 ...