552. Student Attendance Record II

题目: Given a positive integer  n , return the number of all possible attendance records with length n, which will be regarded as rewardable. The answer may be very large, return it after mod 10 9  + 7.

A student attendance record is a string that only contains the following three characters:

  1. 'A' : Absent.
  2. 'L' : Late.
  3. 'P' : Present.

A record is regarded as rewardable if it doesn't contain more than one 'A' (absent) or more than two continuous 'L' (late).

给定一个正整数n,返回长度为n的序列的个数,这个序列是所有可能出席的记录,也就是被认为可以奖赏的。答案可能很大,返回值模上109 + 7。学生出席记录的字符串由‘A’,'L','P'三种字符构成,分别代表出席,迟到和缺席。一条被认为是可奖赏的记录不能超过一次缺席(A)和不能超过连续两个迟到(L)

思路:注意到题目中告诉我们答案可能很大,会比109 + 7还大,所以不能采用穷举法或者递归的方法。鉴于前几周老师一直都在讲动态规划的方法,自己也做了一些练习,所以很快就能想到这道题要用动态规划的方法来解。首先看题目的要求,一条记录当中最多能出现一个A和连续两个L,所以答案跟这两个变量都相关,另外,答案是根据输入的n来输出可能性,所以也与n有关。因此,要用一个三维数组dp[n][i][j]来记录答案,表示长度为n,包含i个A和以连续j个L结尾的字符串所有可能的个数。显然i的取值是0和1,j的取值是0,1,2。因此可以这样初始化dp[1]:dp[1][0][0] = 1; dp[1][0][1] = 1; dp[1][0][2] = 0; dp[1][1][0] = 1; dp[1][1][1] = 0; dp[1][1][2] = 0。  因为字符串长度仅为1,所以出现两个L,一个L和一个A的情况不会出现。接下来讨论状态转移。


状态转移有很多种可能,如果长度为i时,没有出现一个A并且结尾不是L的情况,可以推出长尾为i-1时,也没有出现一个A和结尾不是L,dp[i][0][0] = sum(dp[i-1][0]); 没有出现一个A并且以一个L结尾的情况,可以推出长尾为i-1时,也没有出现一个A和结尾不是L,dp[i][0][1] = dp[i-1][0][0]; 没有出现一个A并且以两个L结尾的情况,可以推出长尾为i-1时,也没有出现一个A和结尾是一个L,dp[i][0][2] = dp[i-1][0][1];  出现了一个A和结尾不是L的情况,可以推出长尾为i-1时,可能没有出现一个A或者出现过一个A并且结尾不是L,dp[i][1][0] = sum(dp[i-1][0]) + sum(dp[i-1][1]); 剩余两种情况与上面类似。

class Solution {
public:
    int sum(int nums[]) {
        long ans = 0;
        for (int i = 0; i < 3; i ++)
            ans += nums[i];
        return ans % 1000000007;
    }
    int checkRecord(int n) {
        int mod = 1000000007;
        int dp[100001][2][3];
        dp[1][0][0] = 1;
        dp[1][0][1] = 1;
        dp[1][0][2] = 0;
        dp[1][1][0] = 1;
        dp[1][1][1] = 0;
        dp[1][1][2] = 0;
        for(int i = 2; i <= n; ++i)
        {
            dp[i][0][0] = sum(dp[i-1][0]);
            dp[i][0][1] = dp[i-1][0][0];
            dp[i][0][2] = dp[i-1][0][1];
            dp[i][1][0] = (sum(dp[i-1][0]) + sum(dp[i-1][1])) % mod;
            dp[i][1][1] = dp[i-1][1][0];
            dp[i][1][2] = dp[i-1][1][1];

        }
        return (sum(dp[n][0]) + sum(dp[n][1])) % mod;
    }
};



虽然这样写通过了,但是感觉不够简洁,因此看了大神们的解法,可以这样子来做。dp[i][j][k]表示长度为i的时候,出现A的个数为j,出现连续k个L。

dp[0]全部初始化为1,转移方程是这样
int val = dp[i-1][j][2];
if (j > 0) val = (val + dp[i-1][j-1][2]) % MOD;
if (k > 0) val = (val + dp[i-1][j][k-1]) % MOD;
dp[i][j][k] = val;



虽然这样写通过了,但是感觉不够简洁,因此看了大神们的解法,可以这样子来做。 dp[i][j][k]表示长度为i的时候,出现A的个数为j,结尾是连续k个L。dp[0]全部初始化为1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值