Student Attendance Record II

Updated on 26 June, 2025
Student Attendance Record II header image

Problem Statement

In this problem, we are given a scenario where a student's attendance is recorded in a string format, with each character representing one of three statuses: Absent ('A'), Late ('L'), and Present ('P'). For a student to be considered for an attendance award, two criteria must be satisfied: the student must have less than two days of absence and must not be late for three consecutive days. Given an integer n, which indicates the length of the attendance record, our task is to compute how many possible attendance records of that length would make the student eligible for the award. Due to potentially large outputs, results should be returned modulo 10^9 + 7.

Examples

Example 1

Input:

n = 2

Output:

8

Explanation:

There are 8 records with length 2 that are eligible for an award:
"PP", "AP", "PA", "LP", "PL", "AL", "LA", "LL"
Only "AA" is not eligible because there are 2 absences (there need to be fewer than 2).

Example 2

Input:

n = 1

Output:

3

Example 3

Input:

n = 10101

Output:

183236316

Constraints

  • 1 <= n <= 105

Approach and Intuition

Understanding this problem requires analyzing how combinations of 'A', 'L', and 'P' can be arranged to satisfy the given criteria for eligibility:

  1. Generate all possible sequences of attendance records of length n.
  2. Ensure that these records have fewer than two 'A's.
  3. Ensure that no sequence contains three or more consecutive 'L's.

The nuances in such sequence generation involve significant combinatorial calculations, especially as n becomes large. Here's the approach:

Dynamic Programming Solution

  • Base Cases: Start with initializing the counts of sequences of shorter lengths which are simpler to calculate manually or have obvious results.
  • Transitional Formula: Establish a relationship where the count for each length n can be derived using the counts from shorter lengths. This typically involves adding a character to shorter valid sequences while maintaining the award criteria.

Modulo Operation

  • Since results can be substantial due to the combinatorial explosion as n increases, and to avoid overflow as well as adhere to problem constraints, we use modulo 10^9 + 7 at every step of our computation.

Edge Cases

  1. Smaller values of n: These are straightforward and serve as base cases for building up to larger n.
  2. Detection of sequences ineligible for an award due to AAA, AAAA (or more continous 'A's) and extensive sequences of 'L': Incorporating mechanisms to skip such combinations saves computation.

By implementing dynamic programming efficiently, these observations can be executed with significant optimizations, leading to feasible solutions even for large values of n, up to the upper constraint of 105.

Solutions

  • C++
  • Java
  • JavaScript
  • Python
cpp
class Solution {
public:
    int countValidSequences(int length) {
        int MODULO = 1000000007;
        vector<vector<int>> currentState = vector<vector<int>>(2, vector<int>(3, 0));
        vector<vector<int>> nextState = vector<vector<int>>(2, vector<int>(3, 0));
        
        currentState[0][0] = 1;

        for (int len = 0; len < length; ++len) {
            for (int absences = 0; absences <= 1; ++absences) {
                for (int lates = 0; lates <= 2; ++lates) {
                    nextState[absences][0] = (nextState[absences][0] + currentState[absences][lates]) % MODULO;
                    if (absences < 1) {
                        nextState[absences + 1][0] = (nextState[absences + 1][0] + currentState[absences][lates]) % MODULO;
                    }
                    if (lates < 2) {
                        nextState[absences][lates + 1] = (nextState[absences][lates + 1] + currentState[absences][lates]) % MODULO;
                    }
                }
            }

            currentState = nextState;
            nextState = vector<vector<int>>(2, vector<int>(3, 0));
        }

        int result = 0;
        for (int absences = 0; absences <= 1; ++absences) {
            for (int lates = 0; lates <= 2; ++lates) {
                result = (result + currentState[absences][lates]) % MODULO;
            }
        }
        return result;
    }
};

The given C++ code defines a solution to the problem of counting valid sequences of student attendance records based on set conditions. The records are determined valid if they contain no more than one 'A' (absence) and more than two continuous 'L' (lates).

Here's a breakdown of the approach and methodology in the code:

  • Initialization: Establishes a 2D vector currentState to keep track of the number of valid sequences for various states pertaining to absences and lates. Similarly, another 2D vector nextState stores the count of sequences for the next day.

  • Dynamic Programming Approach:

    • For each possible sequence length (from 0 to the provided length), iterate through all combinations of absences (0 or 1) and lates (0 to 2).
    • Update nextState based on transitions from 'P' (present), by adding sequences that end with the corresponding absences and lates without exceeding limits.
    • Optionally transition to an 'A' state if current absences are less than 1, making sure not to exceed the absence limit.
    • Optionally add a 'L' to the sequence if the current sequence of lates can be extended without exceeding the limit of 2 consecutive lates.
  • Transition and State Updates:

    • After iterating through all possibilities at a given sequence length, assign the nextState values to currentState, and reset nextState for fresh calculations for the next day (length increment).
  • Result Calculation: Sum up all valid sequences from currentState after processing up to the desired sequence length.

  • Modulo Operation: Apply a modulo operation with MODULO = 1000000007 during calculations to prevent overflow and ensure results fit within standard integer sizes.

  • Return Result: The final result, stored in the variable result, is returned from the function. It represents the count of all valid attendance sequences of the specified length. The function is designed to be efficient and robust enough to handle different input sizes, providing correct count within computational limits.

By using dynamic programming, the code efficiently calculates the number of valid sequences, ensuring quick evaluation as it reuses prior results reducing computing time significantly.

java
class Solution {

    public int validRecord(int maxLen) {
        final int MODULO = 1000000007;
        // Initialize for current calculations of state
        int[][] currentState = new int[2][3];
        // Initialize for future calculations of state
        int[][] nextState = new int[2][3];

        // Starting condition: a string of length 0 without any 'A' and no 'L'.
        currentState[0][0] = 1;

        // Progress through each unit length
        for (int length = 0; length < maxLen; ++length) {
            for (int absences = 0; absences <= 1; ++absences) {
                for (int lates = 0; lates <= 2; ++lates) {
                    // If 'P' is added, reset late count to 0
                    nextState[absences][0] =
                        (nextState[absences][0] +
                            currentState[absences][lates]) % MODULO;
                    // If 'A' is added, increment absence count
                    if (absences < 1) {
                        nextState[absences + 1][0] =
                            (nextState[absences + 1][0] +
                                currentState[absences][lates]) % MODULO;
                    }
                    // If 'L' is added, increment late count
                    if (lates < 2) {
                        nextState[absences][lates + 1] =
                            (nextState[absences][lates + 1] +
                                currentState[absences][lates]) % MODULO;
                    }
                }
            }

            // Move nextState values to currentState for next round
            for (int i = 0; i < currentState.length; i++) {
                System.arraycopy(nextState[i], 0, currentState[i], 0, nextState[i].length);
            }
            // Reset nextState values
            nextState = new int[2][3];
        }

        // Accumulate all possible valid states
        int validCombinations = 0;
        for (int absences = 0; absences <= 1; ++absences) {
            for (int lates = 0; lates <= 2; ++lates) {
                validCombinations = (validCombinations + currentState[absences][lates]) % MODULO;
            }
        }
        return validCombinations;
    }
}

The provided Java solution tackles the problem of counting valid attendance records by managing and constraining the number of absences ('A's) and consecutive late days ('L's) up to a given maximum length of record (maxLen). Each valid record can contain:

  • 'P' indicating presence
  • 'A' indicating absence
  • 'L' indicating late

Constraints

  • You can only have at most one 'A' in the entire record.
  • You cannot have more than two consecutive 'L' characters.

Solution Outline:

  1. Define a dynamic programming approach using a 3D array structure currentState and nextState, which represent the state of the record at any given length. The dimensions represent:

    • Absences count (0 or 1)
    • Consecutive late count (0 to 2)
  2. Initialize currentState for a record of length 0 without any absences or lates, represented as currentState[0][0] = 1.

  3. Utilize nested loops to iterate over every possible state configuration given the constraints:

    • Update nextState considering the addition of 'P', 'A', or 'L':
      • 'P' resets the late count (lates).
      • 'A' increases the absence count (absences), ensuring it doesn’t exceed one.
      • 'L' increases the late count (lates), ensuring it doesn’t exceed two.
    • Employ modulo operation with MODULO = 1000000007 to manage large numbers and prevent overflow.
  4. After calculating potential configurations for a given length, transfer nextState values to currentState to set up calculations for the next potential record length. Reset nextState post transfer.

  5. After iterating up to the desired maxLen, compile the sum of all configurations from currentState that meet the valid constraints to determine the total number of valid attendance records.

  6. Return the count of valid combinations.

This implementation ensures optimal management of all valid sequences through dynamic programming, efficiently iterating through possible configurations and using modulo to maintain manageable number output.

js
var calculateRecord = function (n) {
    const MODULO = 1000000007;
    let currentStateDP = Array.from({ length: 2 }, () => Array(3).fill(0));
    let nextStateDP = Array.from({ length: 2 }, () => Array(3).fill(0));

    currentStateDP[0][0] = 1;

    for (let length = 0; length < n; ++length) {
        for (let absences = 0; absences <= 1; ++absences) {
            for (let lates = 0; lates <= 2; ++lates) {
                nextStateDP[absences][0] =
                    (nextStateDP[absences][0] + currentStateDP[absences][lates]) % MODULO;
                
                if (absences < 1) {
                    nextStateDP[absences + 1][0] =
                        (nextStateDP[absences + 1][0] + currentStateDP[absences][lates]) % MODULO;
                }

                if (lates < 2) {
                    nextStateDP[absences][lates + 1] =
                        (nextStateDP[absences][lates + 1] + currentStateDP[absences][lates]) % MODULO;
                }
            }
        }

        currentStateDP = nextStateDP.map((row) => row.slice());
        nextStateDP = Array.from({ length: 2 }, () => Array(3).fill(0));
    }

    let total = currentStateDP.flat().reduce((accum, value) => (accum + value) % MODULO, 0);
    return total;
};

This JavaScript solution leverages dynamic programming to solve the problem of calculating the total number of valid student attendance records of length n. Each record has constraints on absences ("A") not exceeding one, and consecutive lates ("L") not exceeding two.

  • Initialize constants and states:

    • Define MODULO as 1000000007 to ensure all calculations are performed modulo 10^9 + 7.
    • Create currentStateDP and nextStateDP 2D arrays. Here, the first dimension tracks absences while the second dimension tracks consecutive lates.
  • Set up base cases:

    • Initialize the first attendance record with no absences and no lates, indicated by currentStateDP[0][0] = 1.
  • Iterate through each possible record length:

    • For every day, consider every state (combination of absences and lates) from the current record length.

    • Update nextStateDP for every move:

      • Transition to the next day with one more late, unless already having two consecutive lates.
      • Transition to the next day with one more absence, unless already having one.
      • Move to the next day with no late count.
    • After each iteration, transfer the state from nextStateDP back to currentStateDP for the next day’s calculation, and reset nextStateDP.

  • Compute the total:

    • Sum up all possible states in currentStateDP to get the total number of valid combinations modulo MODULO.
  • Return the total count of valid sequences, ensuring the solution remains efficient and accurately obeys the set rules about attendance recording. This approach guarantees a comprehensive check across all possible sequences of attendance records for a given day length n.

python
class Solution:
    def countRewardableRecords(self, length: int) -> int:
        MODULO = 1000000007
        # Caching current state results for DP.
        current = [[0] * 3 for _ in range(2)]
        # Caching next state results for DP.
        next_state = [[0] * 3 for _ in range(2)]

        # Initialize starting case: empty string scenario.
        current[0][0] = 1

        # Process each potential employee record up to 'length'.
        for _ in range(length):
            for absences in range(2):
                for lates in range(3):
                    # Adding 'P' to the sequence.
                    next_state[absences][0] = (
                        next_state[absences][0]
                        + current[absences][lates]
                    ) % MODULO
                    # Adding 'A' to the sequence if possible.
                    if absences < 1:
                        next_state[absences + 1][0] = (
                            next_state[absences + 1][0]
                            + current[absences][lates]
                        ) % MODULO
                    # Adding 'L' to the sequence if possible.
                    if lates < 2:
                        next_state[absences][lates + 1] = (
                            next_state[absences][lates + 1]
                            + current[absences][lates]
                        ) % MODULO

            # Transition current to next state for next cycle.
            current = [row[:] for row in next_state]
            # Reset next state to zero for next iteration.
            next_state = [[0] * 3 for _ in range(2)]

        # Calculate the total count by summing valid states.
        total_count = (
            sum(
                current[absences][lates]
                for absences in range(2)
                for lates in range(3)
            )
            % MODULO
        )
        return total_count

The presented Python solution effectively calculates how many rewardable attendance records of a given length can be formed using the letters 'P', 'L', and 'A'. Each letter represents an attendance type—'P' for present, 'L' for late, and 'A' for absent. The key constraints are: no more than one 'A' and no more than two consecutive 'L's in the record.

Here's how the implementation works:

  1. Use a dynamic programming (DP) approach where current and next_state arrays help manage the state transitions between different attendance records.
  2. Initialize the DP with the base case where a record of length 0 (an empty string) is considered having one valid formation with zero absences and lates.
  3. Iterate over each length from 1 to the given length and for each possible number of absences (0 or 1) and lates (0 to 2):
    • Update the next_state for adding a 'P', incrementing the count from the same absence and late stage.
    • If absent less than once, update for adding an 'A', resetting lates to zero.
    • If late less than twice consecutively, update for adding an 'L', incrementing the late count.
  4. Transition states by copying next_state to current and resetting next_state.
  5. After finishing the iterations, sum up all valid states' counts from the current configuration to compute the total number of rewardable records modulo 1,000,000,007 to handle very large numbers.

Through this method, you ensure efficient computation and state management that scales with the desired length of records. Every update is modularly reduced to keep computations within numeric limits and ensure accuracy.

Comments

No comments yet.