# Student Attendance Record I
**Difficulty:** EASY
[External](https://leetcode.com/problems/student-attendance-record-i)
Canonical: https://scaleengineer.com/dsa/problems/student-attendance-record-i
**Data structures:** String
---
## Problem
You are given a string `s` representing an attendance record for a student where each character signifies whether the student was absent, late, or present on that day. The record only contains the following three characters:

* `'A'`: Absent.
* `'L'`: Late.
* `'P'`: Present.

The student is eligible for an attendance award if they meet **both** of the following criteria:

* The student was absent (`'A'`) for **strictly** fewer than 2 days **total**.
* The student was **never** late (`'L'`) for 3 or more **consecutive** days.

Return `true` _if the student is eligible for an attendance award, or_ `false` _otherwise_.

**Example 1:**

**Input:** s = "PPALLP"
**Output:** true
**Explanation:** The student has fewer than 2 absences and was never late 3 or more consecutive days.

**Example 2:**

**Input:** s = "PPALLL"
**Output:** false
**Explanation:** The student was late 3 consecutive days in the last 3 days, so is not eligible for the award.

**Constraints:**

* `1 <= s.length <= 1000`
* `s[i]` is either `'A'`, `'L'`, or `'P'`.

# Approaches
## Two-Pass Iteration
This approach involves iterating through the string twice. The first pass checks for the total number of absences, and the second pass checks for consecutive late days. This separation of concerns makes the code easy to read and understand, although it's not the most performant.
**Time:** O(N), where N is the length of the string `s`. In the worst case, we iterate through the string once to count absences and a second time to check for "LLL", resulting in a linear time complexity. · **Space:** O(1), as we only use a constant amount of extra space for counter variables.
**Pros:** The logic is simple and easy to follow because it checks each condition independently.; The code is clean and readable due to the separation of concerns.
**Cons:** It is not the most efficient solution as it requires scanning the string up to two times, whereas a single pass is sufficient.
### Explanation
In this method, we tackle the two eligibility criteria separately in two distinct passes over the input string.

First, we check the condition for absences. We iterate through the string from beginning to end, maintaining a counter for the character 'A'. If the total count of 'A's is 2 or more, we know the student is not eligible, and we can immediately conclude the result is `false`.

If the first pass completes and the number of absences is acceptable (fewer than 2), we proceed to the second pass. In this pass, we check for consecutive lates. A straightforward way to do this is to use a built-in string searching function, like `contains()`, to check for the existence of the substring "LLL". If this substring is found, the student is not eligible, and we return `false`.

If the second pass also completes without finding "LLL", it means neither of the disqualifying conditions was met. Therefore, the student is eligible for the award, and we return `true`.

```java
class Solution {
    public boolean checkRecord(String s) {
        // Pass 1: Check for total absences
        int absentCount = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == 'A') {
                absentCount++;
            }
        }
        if (absentCount >= 2) {
            return false;
        }

        // Pass 2: Check for consecutive lates
        if (s.contains("LLL")) {
            return false;
        }

        return true;
    }
}
```
### Algorithm
- **Pass 1: Check for Absences.**
  1. Initialize `absentCount = 0`.
  2. Iterate through the string `s`. For each character `c`:
     - If `c` is 'A', increment `absentCount`.
  3. If `absentCount >= 2`, the student is not eligible, so return `false`.
- **Pass 2: Check for Consecutive Lates.**
  1. Check if the string `s` contains the substring "LLL".
  2. If it does, the student is not eligible, so return `false`.
- **Conclusion.**
  1. If both passes complete without returning `false`, the student is eligible. Return `true`.

## Regular Expression
This approach leverages the power of regular expressions to solve the problem in a very concise way. We can construct a single regular expression that matches any invalid attendance record. If the input string matches this pattern, the student is not eligible; otherwise, they are.
**Time:** O(N) in most modern regex engines for this type of pattern, where N is the length of the string. The engine effectively scans the string. However, it may have a higher constant factor overhead compared to a simple manual loop. · **Space:** O(1). For this specific pattern, the space used by the regex engine is constant and does not depend on the length of the input string.
**Pros:** Extremely concise and expressive, often reducing the solution to a single line of code.; Leverages powerful built-in features of the language's standard library.
**Cons:** Can be less performant than a direct, manual iteration due to the overhead of the regex engine (parsing the pattern, building the state machine, etc.).; The regex pattern might be less intuitive to read for developers not comfortable with regular expressions.
### Explanation
The core idea is to define a pattern that describes a failing attendance record and check if the input string fits this pattern.

A record fails if either of two conditions is met:
1.  It contains two or more 'A's.
2.  It contains a sequence of three consecutive 'L's.

We can translate these conditions into a regular expression. The pattern for a string containing at least two 'A's can be written as `.*A.*A.*`. This matches any characters (`.`), zero or more times (`*`), followed by an 'A', followed by more characters, followed by a second 'A', and then any remaining characters. The pattern for a string containing "LLL" is simply `.*LLL.*`.

We can combine these two failing patterns using the regex OR operator, `|`. This gives us the final regex: `.*A.*A.*|.*LLL.*`.

We then use the language's built-in regex matching functionality (e.g., `String.matches()` in Java) to test the input string `s` against this pattern. The `matches` method returns `true` if the entire string matches the pattern. Since our pattern identifies invalid strings, we return the negation of the result: `false` if it matches, and `true` if it doesn't.

```java
class Solution {
    public boolean checkRecord(String s) {
        // The regex matches if there are two 'A's anywhere OR if there is "LLL" anywhere.
        // The matches() method checks if the entire string matches the regex.
        // If it matches, the record is invalid, so we return false.
        return !s.matches(".*A.*A.*|.*LLL.*");
    }
}
```
### Algorithm
1. Define a regular expression pattern that matches any invalid attendance record. The pattern for an invalid record is one that contains two or more 'A's (`.*A.*A.*`) or one that contains "LLL" (`.*LLL.*`). These are combined with an OR operator: `.*A.*A.*|.*LLL.*`.
2. Use a built-in regex matching function to test if the input string `s` matches this "invalid" pattern.
3. If the string matches the pattern, the record is invalid, so return `false`.
4. If the string does not match, the record is valid, so return `true`.

## Single-Pass Iteration
This is the most efficient approach. We can verify both eligibility criteria simultaneously by iterating through the string just once. We maintain a count of total absences and a count of consecutive lates in the same loop, allowing for an early exit as soon as an invalidating condition is found.
**Time:** O(N), where N is the length of the string `s`. We iterate through the string only once. · **Space:** O(1), as we only use a constant amount of extra space for our counter variables, regardless of the input string's size.
**Pros:** Most efficient approach in terms of time complexity as it requires only a single pass over the string.; Allows for early exit as soon as an invalidating condition is found, which can save computation on long strings that are invalid early on.; Minimal space usage.
**Cons:** The logic inside the loop is slightly more complex than in a two-pass approach, as it combines multiple checks, but this is a minor trade-off for the performance gain.
### Explanation
This optimal solution processes the string in a single pass, checking both conditions concurrently. This avoids the overhead of multiple iterations.

We iterate through the string from left to right while keeping track of two pieces of information: the total number of absences (`absentCount`) and the current number of consecutive lates (`consecutiveLateCount`).

- When we encounter an 'A', we increment `absentCount`. Since an 'A' breaks any sequence of 'L's, we also reset `consecutiveLateCount` to 0.
- When we see an 'L', we increment `consecutiveLateCount`.
- When we see a 'P', it also breaks a sequence of 'L's, so we reset `consecutiveLateCount` to 0.

After processing each character, we check if either of the award criteria has been violated. If `absentCount` reaches 2 or `consecutiveLateCount` reaches 3, we know the student is ineligible. We can stop processing immediately and return `false`.

If the loop finishes without ever triggering these failure conditions, it means the student's entire record is valid, and we can return `true`.

```java
class Solution {
    public boolean checkRecord(String s) {
        int absentCount = 0;
        int lateCount = 0;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == 'A') {
                absentCount++;
                lateCount = 0; // An absence resets the late streak
            } else if (c == 'L') {
                lateCount++;
            } else { // 'P'
                lateCount = 0; // A present day resets the late streak
            }

            // Check for failure conditions after each character
            if (absentCount >= 2 || lateCount >= 3) {
                return false;
            }
        }
        
        // If the loop completes, the record is valid
        return true;
    }
}
```
### Algorithm
1. Initialize `absentCount = 0` and `consecutiveLateCount = 0`.
2. Iterate through the string `s` from left to right, character by character.
3. For each character `c`:
   - If `c` is 'A', increment `absentCount` and reset `consecutiveLateCount` to 0.
   - If `c` is 'L', increment `consecutiveLateCount`.
   - If `c` is 'P', reset `consecutiveLateCount` to 0.
4. After updating the counts, check if either of the failure conditions is met:
   - `absentCount >= 2`
   - `consecutiveLateCount >= 3`
5. If either condition is true, immediately return `false`.
6. If the loop completes without returning, it means the record is valid. Return `true`.

# Solutions
### Java

```java
class Solution {
public
  boolean checkRecord(String s) {
    return s.indexOf("A") == s.lastIndexOf("A") && !s.contains("LLL");
  }
}

```

### CPP

```cpp
class Solution {
public:
  bool checkRecord(string s) {
    return count(s.begin(), s.end(), 'A') < 2 && s.find("LLL") == string ::npos;
  }
};

```

### Python

```python
class Solution:
    def checkRecord(
        self, s: str) -> bool: return s . count('A') < 2 and 'LLL' not in s

```
