题目
可以用字符串表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤、迟到、到场)。记录中只含下面三种字符:
- ‘A’:Absent,缺勤
- ‘L’:Late,迟到
- ‘P’:Present,到场
如果学生能够 同时 满足下面两个条件,则可以获得出勤奖励:
- 按总出勤计,学生缺勤(‘A’)严格 少于两天。
- 学生不会存在连续 3 天或连续 3 天以上的迟到(‘L’)记录。
给你一个整数 n ,表示出勤记录的长度(次数)。请你返回记录长度为 n 时,可能获得出勤奖励的记录情况 数量 。答案可能很大,所以返回对 109 + 7 取余的结果。
示例 1:
输入:n = 2
输出:8
解释:
有 8 种长度为 2 的记录将被视为可奖励:
"PP" , "AP", "PA", "LP", "PL", "AL", "LA", "LL"
只有"AA"不会被视为可奖励,因为缺勤次数为 2 次(需要少于 2 次)。
示例 2:
输入:n = 1
输出:3
示例 3:
输入:n = 10101
输出:183236316
提示:
- 1 < = n < = 1 0 5 1 <= n <= 10^5 1<=n<=105
题解
方法一:动态规划
因为n最大为
1
0
5
10^5
105,所以肯定就不能用回溯法了,这样会超时,因此选择动态规划。
由于可奖励的出勤记录要求缺勤次数少于 2 和连续迟到次数少于 3,因此动态规划的状态由总天数、缺勤次数和结尾连续迟到次数决定。
定义
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示第 i 天存在 j 次缺勤,结尾连续迟到了 k 次,其中
0
≤
i
≤
n
,
0
≤
j
≤
1
,
0
≤
k
≤
2
0 \leq i \leq n, 0 \leq j \leq 1, 0 \leq k \leq 2
0≤i≤n,0≤j≤1,0≤k≤2。
当 i = 0时,没有出勤记录,此时边界情况是
d
p
[
i
]
[
j
]
[
k
]
=
1
dp[i][j][k]=1
dp[i][j][k]=1。
当
1
≤
i
≤
n
1 \leq i \leq n
1≤i≤n时,
d
p
[
i
]
[
]
[
]
dp[i][][]
dp[i][][]的值由
d
p
[
i
−
1
]
[
]
[
]
dp[i-1][][]
dp[i−1][][]转移得到,计算每个状态的值需要考虑到第 i 天的出勤状况:
- 如果第 i 天的出勤是 ‘P’,那么和第 i - 1天的出勤记录相比,'A’的数量不变,结尾连续’L’的数量清零,因此对于
0
≤
j
≤
1
0 \leq j \leq 1
0≤j≤1,有
d p [ i ] [ j ] [ 0 ] : = d p [ i ] [ j ] [ 0 ] + ∑ k = 0 2 d p [ i − 1 ] [ j ] [ k ] dp[i][j][0] := dp[i][j][0] + \sum_{k = 0}^2dp[i - 1][j][k] dp[i][j][0]:=dp[i][j][0]+k=0∑2dp[i−1][j][k]
比如说对于 d p [ i ] [ 0 ] [ 0 ] = d p [ i − 1 ] [ 0 ] [ 0 ] + d p [ i − 1 ] [ 0 ] [ 1 ] + d p [ i − 1 ] [ 0 ] [ 2 ] dp[i][0][0] = dp[i - 1][0][0] + dp[i - 1][0][1] +dp[i - 1][0][2] dp[i][0][0]=dp[i−1][0][0]+dp[i−1][0][1]+dp[i−1][0][2],表示在过去缺勤次数为0的情况下,可以有0,1,2次连续结尾连续迟到。 - 如果第 i 天的出勤是 ‘A’,那么和第 i - 1天的出勤记录相比,'A’的数量加1,结尾连续’L’的数量清零,这就要求此前 i - 1天的出勤记录中’A’的数量为0,因此有:
d p [ i ] [ 1 ] [ 0 ] : = d p [ i ] [ 1 ] [ 0 ] + ∑ k = 0 2 d p [ i − 1 ] [ 0 ] [ k ] dp[i][1][0] := dp[i][1][0] + \sum_{k = 0}^2dp[i - 1][0][k] dp[i][1][0]:=dp[i][1][0]+k=0∑2dp[i−1][0][k] - 如果第 i 天的出勤是 ‘L’,那么和第 i - 1天的出勤记录相比,'A’的数量不变,结尾连续’L’的数量加1,这就要求此前 i - 1天的出勤记录中结尾连续’L’的数量小于2,也就是第 i 天结尾连续’L’的数量为1或2,因此对于
0
≤
j
≤
1
,
1
≤
k
≤
2
0 \leq j \leq 1, 1 \leq k \leq 2
0≤j≤1,1≤k≤2有:
d p [ i ] [ j ] [ k ] : = d p [ i ] [ j ] [ k ] + d p [ i − 1 ] [ j ] [ k − 1 ] dp[i][j][k] := dp[i][j][k] + dp[i - 1][j][k - 1] dp[i][j][k]:=dp[i][j][k]+dp[i−1][j][k−1]
最后计算长度为n的所有可奖励的出勤记录的数量,计算过程中将结果对 1 0 9 + 7 10^9+7 109+7求模。
class Solution {
public int checkRecord(int n) {
final int MOD = (int)1e9+7;
int[][][] dp = new int[n + 1][2][3]; // dp[i][j][k] 表示在第i天,缺席了j次,以及连续迟到了k次的情况下,还可以拿到奖励的排列数量
dp[0][0][0] = 1;
for(int i = 1; i <= n; i++){
// 以P结尾的数量
for(int j = 0; j <= 1; j++){ // 过去可以是0,1次缺勤
for(int k = 0; k <= 2; k++){
dp[i][j][0] = (dp[i][j][0] + dp[i - 1][j][k]) % MOD; // 表示今天没有缺勤和迟到,有0,1,2次连续迟到
}
}
// 以A结尾的数量
for(int k = 0; k <= 2; k++){
dp[i][1][0] = (dp[i][1][0] + dp[i - 1][0][k]) % MOD; // 表示今天缺勤,过去可以有0,1,2次连续迟到,但是不可以有缺勤
}
// 以L结尾的数量
for(int j = 0; j <= 1; j++){
for(int k = 1; k <= 2; k++){
dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k - 1]) % MOD; // 今天迟到,过去可以在有0,1次缺勤的情况下,有0,1次连续迟到
}
}
}
int sum = 0;
for(int j = 0; j <= 1; j++){
for(int k = 0; k <= 2; k++){
sum = (sum + dp[n][j][k]) % MOD;
}
}
return sum;
}
}
由于 d p [ i ] [ ] [ ] dp[i][][] dp[i][][]只会从 d p [ i − 1 ] [ ] [ ] dp[i-1][][] dp[i−1][][]获得,因此可以省略dp中的总天数维度,优化空间复杂度。
class Solution {
public int checkRecord(int n) {
final int MOD = 1000000007;
int[][] dp = new int[2][3]; // A 的数量,结尾连续 L 的数量
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
int[][] dpNew = new int[2][3];
// 以 P 结尾的数量
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= 2; k++) {
dpNew[j][0] = (dpNew[j][0] + dp[j][k]) % MOD;
}
}
// 以 A 结尾的数量
for (int k = 0; k <= 2; k++) {
dpNew[1][0] = (dpNew[1][0] + dp[0][k]) % MOD;
}
// 以 L 结尾的数量
for (int j = 0; j <= 1; j++) {
for (int k = 1; k <= 2; k++) {
dpNew[j][k] = (dpNew[j][k] + dp[j][k - 1]) % MOD;
}
}
dp = dpNew;
}
int sum = 0;
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= 2; k++) {
sum = (sum + dp[j][k]) % MOD;
}
}
return sum;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/student-attendance-record-ii/solution/xue-sheng-chu-qin-ji-lu-ii-by-leetcode-s-kdlm/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。