↓Skip to main content
  1. LeetCode/

LeetCode 420: Strong Password Checker

·4 mins· ·
LeetCode Hard Greedy String
Wei Yi Chung
Author
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
Table of Contents

Meta Data
#

Difficulty: hard First Attempt: 2026-09-02

  • Submissions: Wrong Answer → Runtime Error ×3 → Wrong Answer → Accepted

Approach
#

Three conditions: length 6 to 20, contains a lowercase letter, an uppercase letter, and a digit, and no three identical characters in a row. You may insert, delete, or replace.

First compute two things:

  • missing: how many character types are missing, 0 to 3.
  • The length L of each run of identical characters (only runs with L >= 3). Fixing a run of length L by replacement takes L // 3 replacements.

Then split by length:

1. Length < 6: you must insert 6 - n characters. Insertions can add missing types and break runs at the same time, so the answer is max(6 - n, missing).

2. Length 6 to 20: no insertions or deletions needed. Each replacement can break a run and add a type, so the answer is max(replacements, missing).

3. Length > 20: you must delete n - 20 characters. Deletions also reduce the replacements needed; the key is where deleting pays off most:

  • Runs with L % 3 == 0: deleting 1 character saves 1 replacement (e.g. 3 → 2 needs 0 instead of 1).
  • Runs with L % 3 == 1: deleting 2 saves 1 replacement.
  • Everything else: every 3 deletions save 1 replacement.

Spend the deletion budget on these three in order. The answer is (n - 20) + max(remaining replacements, missing).

My Attempts
#

Attempt 1: the wrong model
#

It counted repeats with a stack and combined them in one formula, without separating the three length cases or using deletions to cancel replacements.

Runtime Error ×3: typos in variable names
#

After switching to case analysis, three REs in a row:

  1. Using remainDelete before defining it.
  2. Changing it to remainDelete = maxDelete, then misspelling missingType on the next line.
  3. max(replaceCount + missingTypes): max was given a single int.

Wrong Answer: the remaining deletion budget
#

remainDelete = maxDelete              # should be deleteCount - maxDelete

After using maxDelete deletions, the remaining budget is “total deletions minus those used”, not “the number used”.

Solution
#

class Solution:
    def strongPasswordChecker(self, password: str) -> int:
        n = len(password)
        missing = 3 - (any(c.islower() for c in password)
                       + any(c.isupper() for c in password)
                       + any(c.isdigit() for c in password))

        repeats = []
        i = 0
        while i < n:
            j = i
            while j < n and password[j] == password[i]:
                j += 1
            if j - i >= 3:
                repeats.append(j - i)
            i = j

        if n < 6:
            return max(6 - n, missing)

        replace = sum(L // 3 for L in repeats)
        if n <= 20:
            return max(replace, missing)

        delete = n - 20
        remain = delete
        mod0 = sum(1 for L in repeats if L % 3 == 0)
        mod1 = sum(1 for L in repeats if L % 3 == 1)

        used = min(remain, mod0)              # 1 deletion per run saves 1 replacement
        replace -= used
        remain -= used

        used = min(remain, mod1 * 2)          # 2 deletions per run save 1 replacement
        replace -= used // 2
        remain -= used

        replace -= remain // 3                # every other 3 deletions save 1
        return delete + max(replace, missing)

replace can drop below zero, but it is compared with missing (at least 0) at the end, so the answer is unaffected.

Complexity
#

Time O(n), space O(n).

Mistakes to Avoid
#

  • Trying to solve it with one formula instead of three length cases.
  • Not spending deletions in L % 3 priority order.
  • Setting the remaining budget to “the amount used”.
  • Renaming a variable without checking every use.

Interview Explanation
#

I count the missing character types and the runs of length at least 3. Below length 6, insertions add types and break runs at once, so the answer is max(6 - n, missing); between 6 and 20 it is max(replacements, missing). Above 20, n - 20 deletions are mandatory, and they reduce replacements: first runs with L % 3 == 0 (1 deletion saves 1), then L % 3 == 1 (2 save 1), then every 3 deletions save 1. The answer adds the deletions to max(remaining replacements, missing). Time O(n).

Related

LeetCode 2193: Minimum Number of Moves to Make Palindrome
·3 mins
LeetCode Hard Two-Pointers Greedy String Binary-Indexed-Tree
LeetCode 2193 note: greedily fix the outer pair: take the last character and swap its leftmost partner to the front, costing that index; the unpaired character ends in the middle, costing its distance there
LeetCode 3816: Lexicographically Smallest String After Deleting Duplicate Characters
·3 mins
LeetCode Hard Stack Monotonic-Stack Greedy String
LeetCode 3816 note: a monotonic stack that deletes a larger top while another copy exists, then trims duplicate letters off the end since a prefix is always smaller
LeetCode 1147: Longest Chunked Palindrome Decomposition
·2 mins
LeetCode Hard Dynamic-Programming Greedy Two-Pointers Rolling-Hash String
LeetCode 1147 note: build strings inward from both ends and cut a pair as soon as they match; cutting early is best, and any leftover middle counts as one chunk
LeetCode 4026: Maximum Gap Between Stations
·2 mins
LeetCode Medium Two-Pointers Greedy String
LeetCode 4026 note: greedily match from the left for each worker’s earliest station and from the right for the latest; splitting between workers i and i+1, the largest gap is latest[i+1] - earliest[i]
LeetCode 2141: Maximum Running Time of N Computers
·3 mins
LeetCode Hard Greedy Binary-Search Sorting
LeetCode 2141 note: a battery longer than the average can only serve one computer, so remove it with that computer and average the rest, using strict greater-than
LeetCode 726: Number of Atoms
·3 mins
LeetCode Hard Stack String Hash-Map Sorting
LeetCode 726 note: tokenize into elements, numbers, and parentheses; handle parentheses with a stack, with a default multiplier of 1