# LeetCode 551: Student Attendance Record I

## Problem Restatement

We are given a string `s` representing one student's attendance record.

Each character has one meaning:

| Character | Meaning |
|---|---|
| `"A"` | Absent |
| `"L"` | Late |
| `"P"` | Present |

The student gets an attendance award only if both rules are satisfied:

1. The student has strictly fewer than `2` absences in total.
2. The student is never late for `3` or more consecutive days.

Return `true` if the student can receive the award. Otherwise, return `false`.

The official constraints are `1 <= s.length <= 1000`, and every character is one of `"A"`, `"L"`, or `"P"`.

## Input and Output

| Item | Meaning |
|---|---|
| Input | A string `s` |
| Output | A boolean |
| Return `true` when | `s` has at most one `"A"` and does not contain `"LLL"` |
| Return `false` when | `s` has two or more `"A"` characters, or has three consecutive `"L"` characters |

Example function shape:

```python
def checkRecord(s: str) -> bool:
    ...
```

## Examples

Example 1:

```python
s = "PPALLP"
```

There is one absence:

```python
"A" count = 1
```

There are two consecutive late days, but not three:

```python
"LL" exists
"LLL" does not exist
```

So the answer is:

```python
True
```

Example 2:

```python
s = "PPALLL"
```

There is one absence, which is allowed.

But the string contains:

```python
"LLL"
```

That means the student was late for three consecutive days.

So the answer is:

```python
False
```

## First Thought: Direct String Checks

The two award rules map directly to two string checks.

The absence rule says:

```python
s.count("A") < 2
```

The late rule says:

```python
"LLL" not in s
```

The student is eligible only when both are true:

```python
s.count("A") < 2 and "LLL" not in s
```

This is already enough for this problem.

## Algorithm

Use Python's built-in string operations.

1. Count how many times `"A"` appears.
2. Check whether the substring `"LLL"` appears.
3. Return `true` only if the absence count is less than `2` and `"LLL"` is not present.

## Correctness

The award rule has exactly two conditions.

The expression:

```python
s.count("A") < 2
```

is true exactly when the student has zero or one absence. This is exactly the first rule.

The expression:

```python
"LLL" not in s
```

is true exactly when there is no block of at least three consecutive late days. Any longer block, such as `"LLLL"`, also contains `"LLL"` inside it. So this check also rejects four, five, or more consecutive late days.

The algorithm returns the logical `and` of these two checks. Therefore, it returns `true` exactly when both award rules are satisfied, and returns `false` otherwise.

## Complexity

Let `n` be the length of `s`.

| Metric | Value | Why |
|---|---|---|
| Time | `O(n)` | Counting `"A"` scans the string, and checking `"LLL"` also scans the string |
| Space | `O(1)` | Only a few counters and fixed-size checks are needed |

## Implementation

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

## Code Explanation

The first part checks the total absence count:

```python
s.count("A") < 2
```

This allows `0` or `1` absence.

The second part checks the consecutive late rule:

```python
"LLL" not in s
```

This rejects any record containing three consecutive late days.

The `and` operator means the student must satisfy both rules.

## One-Pass Version

We can also solve the problem manually in one pass.

This version is useful when we want to avoid relying on substring search and make the state explicit.

```python
class Solution:
    def checkRecord(self, s: str) -> bool:
        absent_count = 0
        late_streak = 0

        for ch in s:
            if ch == "A":
                absent_count += 1
                late_streak = 0

                if absent_count >= 2:
                    return False

            elif ch == "L":
                late_streak += 1

                if late_streak >= 3:
                    return False

            else:
                late_streak = 0

        return True
```

Here, `absent_count` tracks the total number of absences.

The variable `late_streak` tracks the current consecutive run of `"L"` characters.

Whenever we see `"A"` or `"P"`, the late streak resets to `0`, because the consecutive late run has ended.

## Testing

```python
def run_tests():
    s = Solution()

    assert s.checkRecord("PPALLP") is True
    assert s.checkRecord("PPALLL") is False
    assert s.checkRecord("A") is True
    assert s.checkRecord("AA") is False
    assert s.checkRecord("LL") is True
    assert s.checkRecord("LLL") is False
    assert s.checkRecord("PPLPLPL") is True
    assert s.checkRecord("LALL") is True
    assert s.checkRecord("ALLL") is False

    print("all tests passed")

run_tests()
```

| Test | Why |
|---|---|
| `"PPALLP"` | Valid record from the sample |
| `"PPALLL"` | Invalid because of three consecutive late days |
| `"A"` | One absence is allowed |
| `"AA"` | Two absences are not allowed |
| `"LL"` | Two consecutive late days are allowed |
| `"LLL"` | Three consecutive late days are not allowed |
| `"PPLPLPL"` | Many late days, but not consecutive |
| `"LALL"` | Absence resets the late streak |
| `"ALLL"` | Fails because of `"LLL"` |

