Before attempting this problem, you should be comfortable with:
A valid attendance record has at most 1 absence (A) and no more than 2 consecutive late days (L). We can think of this as a state machine where each state tracks two things: how many absences used so far (0 or 1) and how many consecutive late days at the current position (0, 1, or 2). At each position, we decide whether to place P (present), A (absent), or L (late), and transition to the appropriate next state.
dfs(i, cntA, cntL) where i is the remaining length, cntA is the number of absences used, and cntL is the current consecutive late streak.i == 0, we have built a valid record, so return 1.'P': always valid, resets consecutive late count to 0.'A': only valid if cntA == 0 (no absence used yet), resets late count to 0.'L': only valid if cntL < 2 (fewer than 2 consecutive lates), increments late count.dfs(n, 0, 0) as the answer.class Solution:
def checkRecord(self, n: int) -> int:
MOD = 1000000007
cache = [[[-1 for _ in range(3)] for _ in range(2)] for _ in range(n + 1)]
def dfs(i, cntA, cntL):
if i == 0:
return 1
if cache[i][cntA][cntL] != -1:
return cache[i][cntA][cntL]
res = dfs(i - 1, cntA, 0) % MOD
if cntA == 0:
res = (res + dfs(i - 1, 1, 0)) % MOD
if cntL < 2:
res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD
cache[i][cntA][cntL] = res
return res
return dfs(n, 0, 0)This approach reorganizes the state representation. Instead of tracking individual decisions, we store a 2D map keyed by (absence count, consecutive late count) for each length. The recursive function returns all six possible state counts for strings of length n, and we combine them when extending to longer strings.
n == 1, manually define the counts for each (A, L) state.n, recursively get the state counts for n - 1.'P' sums all states with the same absence count (resets late to 0).'L' shifts the late count up by 1.'A' moves from absence count 0 to 1 (resets late to 0).class Solution:
def checkRecord(self, n: int) -> int:
MOD = 10**9 + 7
cache = {}
def count(n):
if n == 1:
# (A, L)
return {
(0, 0): 1, (0, 1): 1, (0, 2): 0,
(1, 0): 1, (1, 1): 0, (1, 2): 0
}
if n in cache:
return cache[n]
tmp = count(n - 1)
res = defaultdict(int)
# Choose P
res[(0, 0)] = ((tmp[(0, 0)] + tmp[(0, 1)]) % MOD + tmp[(0, 2)]) % MOD
res[(1, 0)] = ((tmp[(1, 0)] + tmp[(1, 1)]) % MOD + tmp[(1, 2)]) % MOD
# Choose L
res[(0, 1)] = tmp[(0, 0)]
res[(0, 2)] = tmp[(0, 1)]
res[(1, 1)] = tmp[(1, 0)]
res[(1, 2)] = tmp[(1, 1)]
# Choose A
res[(1, 0)] += ((tmp[(0, 0)] + tmp[(0, 1)]) % MOD + tmp[(0, 2)]) % MOD
cache[n] = res
return res
return sum(count(n).values()) % MODInstead of recursion, we iterate forward and build the DP table from length 0 to n. Each state dp[i][cntA][cntL] represents the number of valid records of length i that have used cntA absences and end with cntL consecutive lates.
dp[0][0][0] = 1 (one way to have an empty record with no absences and no lates).1 to n, for each state (cntA, cntL):'P': transitions from any late count to late count 0 with the same absence count.'A': if cntA > 0, transitions from absence count cntA - 1 to cntA with late count 0.'L': if cntL > 0, transitions from late count cntL - 1 to cntL with the same absence count.n for the final answer.class Solution:
def checkRecord(self, n: int) -> int:
MOD = 1000000007
dp = [[[0 for _ in range(3)] for _ in range(2)] for _ in range(n + 1)]
dp[0][0][0] = 1 # Base case
for i in range(1, n + 1):
for cntA in range(2):
for cntL in range(3):
# Choose P
dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD
# Choose A
if cntA > 0:
dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD
# Choose L
if cntL > 0:
dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD
return sum(dp[n][cntA][cntL] for cntA in range(2) for cntL in range(3)) % MODSince each position only depends on the previous position, we can reduce space from O(n) to O(1) by keeping only two layers: the current and previous states. We store a 2x3 array representing all six possible (absence count, late count) combinations.
1: set counts for each (A, L) state.2 to n):'P', 'L', or 'A'.'P' sums all late counts for the same absence count (resets late to 0).'L' shifts late count forward.'A' transfers from absence 0 to absence 1.class Solution:
def checkRecord(self, n: int) -> int:
if n == 1:
return 3
MOD = 10**9 + 7
dp = {
(0, 0): 1, (0, 1): 1, (0, 2): 0,
(1, 0): 1, (1, 1): 0, (1, 2): 0
}
for i in range(n - 1):
ndp = defaultdict(int)
# Choose P
ndp[(0, 0)] = ((dp[(0, 0)] + dp[(0, 1)]) % MOD + dp[(0, 2)]) % MOD
ndp[(1, 0)] = ((dp[(1, 0)] + dp[(1, 1)]) % MOD + dp[(1, 2)]) % MOD
# Choose L
ndp[(0, 1)] = dp[(0, 0)]
ndp[(1, 1)] = dp[(1, 0)]
ndp[(0, 2)] = dp[(0, 1)]
ndp[(1, 2)] = dp[(1, 1)]
# Choose A
ndp[(1, 0)] = (ndp[(1, 0)] + (((dp[(0, 0)] + dp[(0, 1)]) % MOD + dp[(0, 2)]) % MOD)) % MOD
dp = ndp
return sum(dp.values()) % MODThis is an alternative space-optimized formulation that iterates forward from the empty state. We maintain a 2x3 DP array and update it in place, swapping between current and next arrays each iteration.
dp[0][0] = 1 (empty record with no absences and no consecutive lates).1 to n:'P': adds to state (cntA, 0) from any (cntA, cntL).'A': adds to state (cntA, 0) from (cntA - 1, cntL) if cntA > 0.'L': adds to state (cntA, cntL) from (cntA, cntL - 1) if cntL > 0.class Solution:
def checkRecord(self, n: int) -> int:
MOD = 1000000007
dp = [[0] * 3 for _ in range(2)]
dp[0][0] = 1 # Base case
for i in range(1, n + 1):
next_dp = [[0] * 3 for _ in range(2)]
for cntA in range(2):
for cntL in range(3):
# Choose P
next_dp[cntA][0] = (next_dp[cntA][0] + dp[cntA][cntL]) % MOD
# Choose A
if cntA > 0:
next_dp[cntA][0] = (next_dp[cntA][0] + dp[cntA - 1][cntL]) % MOD
# Choose L
if cntL > 0:
next_dp[cntA][cntL] = (next_dp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD
dp = next_dp
return sum(dp[cntA][cntL] for cntA in range(2) for cntL in range(3)) % MODWhen placing a 'P' (present) or 'A' (absent), the consecutive late count resets to 0. A common mistake is to carry forward the late count, which leads to undercounting valid sequences. Only placing 'L' increments the late counter; all other characters reset it.
The problem states that a valid record has fewer than 2 absences, meaning at most 1 absence is allowed. Tracking the absence count incorrectly or allowing transitions that would result in 2 or more absences will produce an inflated count.
The number of valid records grows exponentially with n, and the problem requires the result modulo 10^9 + 7. Failing to apply the modulo operation after each addition can cause integer overflow, especially in languages with fixed-size integers like Java or C++.
The late count can only be 0, 1, or 2. When adding 'L', you transition from late count k to k+1, but only if k < 2. Allowing a transition to late count 3 or mishandling the boundary condition will break the constraint of no more than 2 consecutive lates.
The DP state requires tracking both the absence count (0 or 1) and the consecutive late count (0, 1, or 2), giving 6 total states per position. Collapsing these into fewer states or incorrectly indexing the state array leads to wrong answers.