1. 程式人生 > >Leetcode 552. Student Attendance Record II

Leetcode 552. Student Attendance Record II

Problem:

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 109 + 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).

Example 1:

Input: n = 2
Output: 8 
Explanation:
There are 8 records with length 2 will be regarded as rewardable:
"PP" , "AP", "PA", "LP", "PL", "AL", "LA", "LL"
Only "AA" won't be regarded as rewardable owing to more than one absent times. 

Note: The value of n won't exceed 100,000.

 

Solution:

  這道題先說說我的做法吧(貌似我自己想出來的解法效率都不高。。。),首先這道題肯定用動態規劃,他有兩個約束條件,先來處理第一個條件,是否存在缺席,因此我維護了兩個陣列,hasAbsent表示存在一次缺席的情況下分別以A,L,P結尾的可能性數量,noAbsent表示不存在缺席的情況下以L,P結尾的可能性數量

1. 對於hasAbsent[i][0],即存在缺席以A結尾的數量等於noAbsent[i-1][0]+noAbsent[i-1][1]加上最後一個A

2. 對於hasAbsent[i][1],即存在缺席以L結尾的數量可以分五種情況

  (1)若i-2之前存在缺席,則有...A_L;...L_L;...P_L這三種可能性,第一種下劃線處可以填P,L,第二種下劃線只能填P,第三種下劃線可以填P,L。

  (2)若i-2之前不存在缺席,則有...LAL;...PAL這兩種可能,

 因此hasAbsent[i][1] = (hasAbsent[i-2][0]*2+hasAbsent[i-2][1]+hasAbsent[i-2][2]*2+noAbsent[i-2][0]+noAbsent[i-2][1])%MOD;

3.對於hasAbsent[i][2],即存在缺席以P結尾的數量等於i-1之前存在缺席的所有情況加上結尾的P,

4.對於noAbsent[i][0],即不存在缺席以L結尾的數量分兩種情況,...L_L;...P_L,第一種情況下劃線可以填P,第二種下劃線可以填L,P,所以noAbsent[i][0] = (noAbsent[i-2][0]+noAbsent[i-2][1]*2)%MOD;

5.對於noAbsent[i][1],即不存在缺席以P結尾的數量等於i-1時所有不存在缺席的可能性之和。

程式碼如下,時間複雜度為O(n),能夠AC,但是效率不高,原因在於乘法和加法運算有點多。

 1 class Solution {
 2 public:
 3     int checkRecord(int n) {
 4         int MOD = 1000000007;
 5         vector<vector<int64_t>> hasAbsent(n,vector<int64_t>(3,0));
 6         vector<vector<int64_t>> noAbsent(n,vector<int64_t>(2,0));
 7         if(n == 1) return 3;
 8         if(n == 2) return 8;
 9         hasAbsent[0] = {1,0,0};
10         noAbsent[0] = {1,1};
11         hasAbsent[1] = {2,1,1};
12         noAbsent[1] = {2,2};
13         for(int i = 2;i != n;++i){
14             hasAbsent[i][0] = (noAbsent[i-1][0]+noAbsent[i-1][1])%MOD;
15             hasAbsent[i][1] = (hasAbsent[i-2][0]*2+hasAbsent[i-2][1]+hasAbsent[i-2][2]*2+noAbsent[i-2][0]+noAbsent[i-2][1])%MOD;
16             hasAbsent[i][2] = (hasAbsent[i-1][0]+hasAbsent[i-1][1]+hasAbsent[i-1][2])%MOD;
17             noAbsent[i][0] = (noAbsent[i-2][0]+noAbsent[i-2][1]*2)%MOD;
18             noAbsent[i][1] = (noAbsent[i-1][0]+noAbsent[i-1][1])%MOD;
19         }
20         return (hasAbsent[n-1][0]+hasAbsent[n-1][1]+hasAbsent[n-1][2]+noAbsent[n-1][0]+noAbsent[n-1][1])%MOD;
21     }
22 };

  接下來看一種思路非常清晰,效率又非常高的解法,參考這裡。解釋下他在做什麼,P[i]記錄了以P結尾不存在A的在i處結尾的可能性,PorL[i]記錄了以P或L結尾i的不存在A的在i處結尾的可能性,很顯然,對於PorL[i],存在三種情況,...P;...PL;...PLL,因此PorL[i] = (P[i] + P[i - 1] + P[i - 2])。接下來需要對PorL陣列進行處理,我們在任意位置用A進行替換,那麼可能性之和即左右兩部分的長度的可能性之積,即PorL[i] * PorL[n - 1 - i]

Code:

 

class Solution {
public:
    int checkRecord(int n) {
        int M = 1000000007;
        vector<long long> P(n + 1), PorL(n + 1);
        P[0] = 1; PorL[0] = 1; PorL[1] = 2;
        for (int i = 1; i <= n; ++i) {
            P[i] = PorL[i - 1];
            if (i > 1) PorL[i] = (P[i] + P[i - 1] + P[i - 2]) % M;
        }
        long long res = PorL[n];
        for (int i = 0; i < n; ++i) {
            long long t = (PorL[i] * PorL[n - 1 - i]) % M;
            res = (res + t) % M;
        }
        return res;
    }
};