▶ 一个学生的考勤状况是一个字符串,其中各字符的含义是:A 缺勤,L 迟到,P 正常。如果一个学生考勤状况中 A 不超过一个,且没有连续两个 L(L 可以有多个,但是不能连续),则称该学生达标(原文表述:A student could be rewarded if his attendance record doesn't contain more than one 'A' (absent) or more than two continuous 'L' (late). )
▶ 551. 给定一个学生的考勤状况,判断他能否达标。
● 代码,4 ms,简单的计数,最快的解法算法与之相同
1 class Solution 2 { 3 public: 4 bool checkRecord(string s) 5 { 6 int i, a, l; 7 for (i = a = l = 0; i < s.size(); i++) 8 { 9 if (s[i] == 'A') 10 a++; 11 if (s[i] == 'L') 12 l++; 13 else // 出现 A 或 P 时均要清空 L 计数 14 l = 0; 15 if (a >= 2 || l > 2) 16 return false; 17 } 18 return true; 19 } 20 };
▶ 552. 给定正整数 n,考虑所有长度为 n 的考勤状况(3n 种可能),计算达标的考勤状况数,该数字可能非常大,取关于 109 + 7(竟然是个素数)模作为输出结果
● 自己的代码,29 ms,时间复杂度 O(n),考虑递推数列,一个达标序列只能是以下三种情况:
a[ k ] 表长度为 k,以 P 结尾,不含 A 的达标序列个数
b[ k ] 表长度为 k,以 PL 结尾,不含 A 的达标序列个数(不能是 LLLP 结尾)
c[ k ] 表长度为 k,以 LL 结尾,不含 A 的达标序列个数(不能是 LLL 结尾)
注意到 a[ 1 ] = b[ 1 ] = 1,c[ 1 ] = 0,a[ k ] = a[ k - 1 ] + b[ k - 1 ] + c[ k - 1](任意一种达标序列添个 P),b[ k ] = a[ k - 1 ](P 结尾的达标序列添个 L),c[ k ] = b[ k - 1 ](PL 结尾的达标序列添个 L)
约化后就变成了 a[ 1 ] = 1,a[ 2 ] = 2,a[ 3 ] = 4,a[ n ] = a[ n - 1 ] + a[ n - 2 ] + a[ n - 3 ] ( n ≥ 4 )
对上述数列打表,使用长度为 n+1 的表 f,其中 a[ k ] = f[ k - 1 ],即所有下标有一单位的平移,在代码中注意控制
因为打表序列中 A 至多一个,所以按照 A 的位置分类讨论:
① 不含A,一共有 a[ n ] + b[ n ] + c[ n ] = a[ n + 1 ] 种情况,等于 f[ n ](打表要长一格的原因)
② A 在开头,一共有 a[ n - 1 ] + b[ n - 1 ] + c[ n - 1 ] = a[ n ] 种情况,等于 f[ n - 1 ]
③ A 在结尾,同上,等于 f[ n - 1 ]
④ A 在中间,考虑 A 的左边有长度为 i 的不含 A 的序列,且 A 的右边有长度为 n - i - 1 的不含A的序列,一共是 ( a[ i ] + b[ i ] + c[ i ] ) * ( a[ n - i - 1 ] + b[ n - i - 1 ] + c[ n - i - 1 ] ),即 f[ i ] * f[ n - i - 1 ],
i 在 i = 1 到 i = n - 2 之间求和。
注意每一步取模运算,因为 f 本身是指数级增长的。
1 class Solution 2 { 3 public: 4 int checkRecord(int n) 5 { 6 if (n == 1) 7 return 3; 8 const int m = 1000000007; 9 vector<long long> f(n + 1, 0); 10 int i,sum; 11 for (f[0] = 1, f[1] = 2, f[2] = 4, i = 3; i <= n; f[i] = (f[i - 1] + f[i - 2] + f[i - 3]) % m, i++); 12 for (sum = (f[n] + f[n - 1] * 2) % m, i = 1; i < n - 1; i++) 13 sum = (sum + f[i] * f[n - i - 1]) % m; 14 return sum; 15 } 16 };
● 代码,4 ms,使用一个矩阵的幂来计算递推
1 #define ENTRY(A, i, j) ((A)[(i) * n + (j)]) 2 3 int64_t* NewMatrix(size_t n) 4 { 5 assert(n >= 1); 6 int64_t* temp = (int64_t*)calloc(n * n, sizeof(int64_t)); 7 assert(temp != NULL); 8 return temp; 9 } 10 11 void FreeMatrix(int64_t* A) 12 { 13 free(A); 14 } 15 16 void SetMatrix(int64_t* A, int64_t* B, size_t n) 17 { 18 memcpy(A, B, n * n * sizeof(int64_t)); 19 } 20 21 void IdentityMatrix(int64_t* A, size_t n)// 将方阵 A 赋值为单位方阵 22 { 23 int i, j; 24 for (i = 0; i < n; i++) 25 { 26 for (j = 0; j < n; j++) 27 ENTRY(A, i, j) = (i == j); 28 } 29 } 30 31 void MatrixMultiply(int64_t* A, int64_t* B, size_t n)// 方阵乘法 A = A * B 32 { 33 assert(n >= 1); 34 int64_t* C = NewMatrix(n); 35 int i, j, k; 36 for (i = 0; i < n; ++i) 37 { 38 for (j = 0; j < n; ++j) 39 { 40 ENTRY(C, i, j) = 0; 41 for (k = 0; k < n; ++k) 42 ENTRY(C, i, j) += ENTRY(A, i, k) * ENTRY(B, k, j); 43 } 44 } 45 memcpy(A, C, n * n * sizeof(int64_t)); 46 FreeMatrix(C); 47 } 48 49 void MatrixPower(int64_t* C, int64_t* A, size_t n, int m)// 方阵幂 C = A ^ m 50 { 51 assert(n >= 1); 52 assert(m >= 0); 53 int64_t* B = NewMatrix(n); 54 SetMatrix(B, A, n); 55 IdentityMatrix(C, n); 56 for (; m > 0; m /= 2)// 二进制方法 57 { 58 if (m % 2 == 1) 59 MatrixMultiply(C, B, n); 60 MatrixMultiply(B, B, n); 61 } 62 FreeMatrix(B); 63 } 64 65 void MatrixModulus(int64_t* A, size_t n, int64_t modulus)// 方阵逐格求模 A %= m 66 { 67 int i, j; 68 for (i = 0; i < n; ++i) 69 { 70 for (j = 0; j < n; ++j) 71 ENTRY(A, i, j) = ENTRY(A, i, j) % modulus; 72 } 73 } 74 75 void MatrixModulusPower(int64_t* C, int64_t* A, size_t n, int m, int64_t modulus)// C = A ^ m % modulus 76 { 77 assert(n >= 1); 78 assert(m >= 0); 79 int64_t* B = NewMatrix(n); 80 SetMatrix(B, A, n); 81 IdentityMatrix(C, n); 82 for (; m > 0;m/=2) 83 { 84 if (m % 2 == 1) 85 { 86 MatrixMultiply(C, B, n); 87 MatrixModulus(C, n, modulus); 88 } 89 MatrixMultiply(B, B, n); 90 MatrixModulus(B, n, modulus); 91 } 92 FreeMatrix(B); 93 } 94 95 int AttendanceNumber(int m) 96 { 97 assert(m >= 0); 98 size_t n = 6; 99 int64_t initial_vector[6] = { 1, 3, 8, 19, 43, 94 }; 100 int64_t modulus = 1000000007; 101 int64_t* A = NewMatrix(n); 102 ENTRY(A, 0, 1) = 1; 103 ENTRY(A, 1, 2) = 1; 104 ENTRY(A, 2, 3) = 1; 105 ENTRY(A, 3, 4) = 1; 106 ENTRY(A, 4, 5) = 1; 107 ENTRY(A, 5, 0) = -1; 108 ENTRY(A, 5, 1) = -2; 109 ENTRY(A, 5, 2) = -3; 110 ENTRY(A, 5, 3) = 0; 111 ENTRY(A, 5, 4) = 1; 112 ENTRY(A, 5, 5) = 2; 113 int64_t answer = 0; 114 if (m <= 5) 115 answer = initial_vector[m]; 116 else 117 { 118 int64_t* C = NewMatrix(n); 119 MatrixModulusPower(C, A, n, m - n + 1, modulus); 120 for (size_t i = 0; i < n; ++i) 121 answer += ENTRY(C, n - 1, i) * initial_vector[i]; 122 answer = (answer % modulus + modulus) % modulus; 123 FreeMatrix(C); 124 } 125 FreeMatrix(A); 126 return answer; 127 } 128 129 #undef ENTRY 130 131 class Solution 132 { 133 public: 134 int checkRecord(int n) 135 { 136 return AttendanceNumber(n); 137 } 138 };
● 初版动态规划(MLE),f[ i ][ j ][ k ] 表示长度为 i,至多 j 个 A,至多 k 个连续 L 的序列。所求目标为 f[ n ][ 1 ][ 2 ],转移方程如下,发现可以用矩阵幂来一次性计算 f[ n ]
1 class Solution 2 { 3 public: 4 int checkRecord(int n) 5 { 6 const int MOD = 1000000007; 7 vector<vector<vector<int>>> f(n + 1, vector<vector<int>>(2, vector<int>(3, 0))); 8 int i, j, k, val; 9 for (i = 0; i < 2; i++) 10 for (j = 0; j < 3; f[0][i][j++] = 1); 11 for (i = 1; i <= n; i++) 12 { 13 for (j = 0; j < 2; j++) 14 { 15 for (k = 0; k < 3; k++) 16 { 17 val = f[i - 1][j][2]; // P 18 if (j > 0) // A 19 val = (val + f[i - 1][j - 1][2]) % MOD; 20 if (k > 0) // L 21 val = (val + f[i - 1][j][k - 1]) % MOD; 22 f[i][j][k] = val; 23 } 24 } 25 } 26 return f[n][1][2]; 27 } 28 };
● 改进动态规划,8 ms,直接用矩阵幂来计算
1 class Solution 2 { 3 public: 4 const int MOD = 1000000007, M = 6; 5 void mul(vector<vector<int>>& A, vector<vector<int>>& B) 6 { 7 vector<vector<int>> temp(M, vector<int>(M, 0)); 8 int i, j, k; 9 for (i = 0; i < M; i++) 10 { 11 for (j = 0; j < M; j++) 12 { 13 for (k = 0; k < M; k++) 14 temp[i][j] = (int)((temp[i][j] + (long)A[i][k] * B[k][j]) % MOD); 15 } 16 } 17 A = temp; 18 return; 19 } 20 void pow(vector<vector<int>>& A, int n) 21 { 22 vector<vector<int>> temp(M, vector<int>(M, 0)); 23 for (int i = 0; i < M; temp[i][i] = 1, i++); 24 for (; n > 0; n /= 2) 25 { 26 if (n % 2) 27 mul(temp, A); 28 mul(A, A); 29 } 30 A = temp; 31 return; 32 } 33 int checkRecord(int n) 34 { 35 vector<vector<int>> A = 36 { 37 { 0, 0, 1, 0, 0, 0 }, 38 { 1, 0, 1, 0, 0, 0 }, 39 { 0, 1, 1, 0, 0, 0 }, 40 { 0, 0, 1, 0, 0, 1 }, 41 { 0, 0, 1, 1, 0, 1 }, 42 { 0, 0, 1, 0, 1, 1 }, 43 }; 44 pow(A, n + 1); 45 return A[5][2]; 46 } 47 };
● 代码,深度优先遍历,TLE,说明穷举肯定不行
1 class Solution 2 { 3 public: 4 int checkRecord(int n) 5 { 6 long long res = 0; 7 dfs(n, 0, 0, 0, res); 8 return res % 1000000007; 9 } 10 void dfs(int n, int s, int A, int L, long long& res)// 当前共 s 位,有 A 个 A 和 L 个 L 11 { 12 if (s == n) 13 { 14 res++; 15 return; 16 } 17 dfs(n, s + 1, A, 0, res); //add "P" 18 if (A < 1) 19 dfs(n, s + 1, A + 1, 0, res); 20 if (L <= 1) 21 dfs(n, s + 1, A, L + 1, res); 22 } 23 };