HomeLeetcode420. Strong Password Checker - Leetcode Solutions

420. Strong Password Checker – Leetcode Solutions

Description

A password is considered strong if the below conditions are all met:

  • It has at least 6 characters and at most 20 characters.
  • It contains at least one lowercase letter, at least one uppercase letter, and at least one digit.
  • It does not contain three repeating characters in a row (i.e., "Baaabb0" is weak, but "Baaba0" is strong).

Given a string password, return the minimum number of steps required to make password strong. if password is already strong, return 0.

In one step, you can:

  • Insert one character to password,
  • Delete one character from password, or
  • Replace one character of password with another character.

Examples:

Example 1:

Input: password = "a"
Output: 5

Example 2:

Input: password = "aA1"
Output: 3

Example 3:

Input: password = "1337C0d3"
Output: 0

Solution in Python

Approach:

To determine the minimum steps to make a password strong:

  1. Check Length Constraints:
    • Passwords must have a length between 6 and 20 characters.
    • If the length is less than 6, insertions are needed.
    • If the length is greater than 20, deletions are needed.
  2. Check Character Requirements:
    • Check if the password contains at least one lowercase letter, one uppercase letter, and one digit.
  3. Check Repeated Characters:
    • Identify sequences of three or more repeated characters. Break these sequences using replacements or deletions.
  4. Calculate Total Steps:
    • Combine the steps needed for length adjustments, character requirements, and removing/replacing repeating characters.
Python
class Solution:
    def strongPasswordChecker(self, password: str) -> int:
        # Initialize variables
        n = len(password)
        has_lower = False
        has_upper = False
        has_digit = False
        change_count = 0
        
        # Check for lowercase, uppercase, and digit presence
        for char in password:
            if char.islower():
                has_lower = True
            elif char.isupper():
                has_upper = True
            elif char.isdigit():
                has_digit = True
        
        # Count missing character types
        missing_types = int(not has_lower) + int(not has_upper) + int(not has_digit)
        
        # Step 1: Handle repeated characters
        replace = 0
        one_seq = two_seq = 0  # To track sequences of length % 3 == 1 or 2
        i = 2  # Start checking from the third character
        
        while i < n:
            if password[i] == password[i - 1] == password[i - 2]:  # Repeated character
                length = 2  # Length of the current sequence
                while i < n and password[i] == password[i - 1]:
                    length += 1
                    i += 1
                
                replace += length // 3
                if length % 3 == 0:
                    one_seq += 1
                elif length % 3 == 1:
                    two_seq += 1
            else:
                i += 1
        
        # Step 2: Handle length constraints
        if n < 6:
            # If password is too short, we need to add characters
            return max(6 - n, missing_types)
        elif n <= 20:
            # If password length is valid, replacements handle missing types and repeats
            return max(replace, missing_types)
        else:
            # If password is too long, deletions are required
            delete_count = n - 20
            
            # Optimize deletions to reduce replacements
            replace -= min(delete_count, one_seq * 1) // 1
            replace -= min(max(delete_count - one_seq, 0), two_seq * 2) // 2
            replace -= max(delete_count - one_seq - 2 * two_seq, 0) // 3
            
            return delete_count + max(replace, missing_types)

Explanation of the Code:

  1. Character Type Check:
    • The loop counts how many character types are missing (lowercase, uppercase, digits).
    • missing_types holds the total number of character types that need to be added.
  2. Repeated Characters:
    • Sequences of three or more repeated characters are identified.
    • These sequences require replacements, but we prioritize breaking them based on their length modulo 3 to optimize deletions in the next step.
  3. Length Constraints:
    • If the password is shorter than 6 characters, the required steps are the maximum of missing character types and the characters needed to reach length 6.
    • If the password length is between 6 and 20, only replacements are needed.
    • If the password is longer than 20, deletions are prioritized to reduce the length, and replacements handle the remaining issues.
  4. Optimize Steps:
    • For overly long passwords, deletions are used strategically to minimize replacements for breaking repeated sequences.

Solution in C++

C++
class Solution {
public:
    int strongPasswordChecker(string password) {
        int n = password.length();

        // Step 1: Check for the presence of lowercase, uppercase, and digit
        bool hasLower = false, hasUpper = false, hasDigit = false;
        for (char c : password) {
            if (islower(c)) hasLower = true;
            if (isupper(c)) hasUpper = true;
            if (isdigit(c)) hasDigit = true;
        }

        // Count missing character types
        int missingTypes = (!hasLower) + (!hasUpper) + (!hasDigit);

        // Step 2: Count sequences of three or more repeating characters
        int replaceCount = 0; // Number of replacements to break repeating sequences
        int oneMod = 0, twoMod = 0; // Track lengths % 3 == 1 or 2
        for (int i = 2; i < n; ++i) {
            if (password[i] == password[i - 1] && password[i] == password[i - 2]) {
                int length = 2; // Start with 3 repeating characters
                while (i < n && password[i] == password[i - 1]) {
                    ++length;
                    ++i;
                }
                replaceCount += length / 3;
                if (length % 3 == 0) ++oneMod;
                else if (length % 3 == 1) ++twoMod;
            }
        }

        // Step 3: Handle different password lengths
        if (n < 6) {
            // If password is too short, we need to add characters
            return max(6 - n, missingTypes);
        } else if (n <= 20) {
            // If password length is valid, replacements handle repeating characters
            return max(missingTypes, replaceCount);
        } else {
            // If password is too long, we need to delete characters
            int deleteCount = n - 20;
            // Optimize replacements by using deletions
            replaceCount -= min(deleteCount, oneMod * 1) / 1;
            replaceCount -= min(max(deleteCount - oneMod, 0), twoMod * 2) / 2;
            replaceCount -= max(deleteCount - oneMod - 2 * twoMod, 0) / 3;

            return deleteCount + max(missingTypes, replaceCount);
        }
    }
};

Solution in Javascript

JavaScript
/**
 * @param {string} password
 * @return {number}
 */
var strongPasswordChecker = function(password) {
    const n = password.length;

    // Step 1: Check for the presence of lowercase, uppercase, and digit
    let hasLower = false, hasUpper = false, hasDigit = false;

    // Loop through the password to check for the required characters
    for (let i = 0; i < n; i++) {
        let c = password[i];
        if (/[a-z]/.test(c)) hasLower = true;
        if (/[A-Z]/.test(c)) hasUpper = true;
        if (/\d/.test(c)) hasDigit = true;
    }

    // Count how many character types are missing
    let missingTypes = 0;
    if (!hasLower) missingTypes++;
    if (!hasUpper) missingTypes++;
    if (!hasDigit) missingTypes++;

    // Step 2: Count sequences of three or more repeating characters
    let replaceCount = 0; // Number of replacements to break repeating sequences
    let oneMod = 0, twoMod = 0; // Track lengths % 3 == 1 or 2
    let i = 2;

    // Iterate through the password starting from the third character
    while (i < n) {
        if (password[i] === password[i - 1] && password[i] === password[i - 2]) {
            let length = 2; // Start with length 2 because we know we already have 3 repeating characters
            while (i < n && password[i] === password[i - 1]) {
                length++;
                i++;
            }
            replaceCount += Math.floor(length / 3); // For each sequence of length >= 3, add the number of replacements required

            // Track how many sequences are of lengths % 3 == 0 or 1
            if (length % 3 === 0) oneMod++;
            else if (length % 3 === 1) twoMod++;
        } else {
            i++;
        }
    }

    // Step 3: Handle different password lengths
    if (n < 6) {
        // If password is too short, we need to add characters
        return Math.max(6 - n, missingTypes);
    } else if (n <= 20) {
        // If password length is valid, replacements handle repeating characters
        return Math.max(missingTypes, replaceCount);
    } else {
        // If password is too long, we need to delete characters
        let deleteCount = n - 20;

        // Optimize replacements by using deletions
        replaceCount -= Math.min(deleteCount, oneMod); // Use 1 deletion for each group % 3 == 0
        replaceCount -= Math.floor(Math.min(Math.max(deleteCount - oneMod, 0), twoMod * 2) / 2); // Use 2 deletions for groups % 3 == 1
        replaceCount -= Math.floor(Math.max(deleteCount - oneMod - 2 * twoMod, 0) / 3); // Use 3 deletions for groups % 3 == 2

        return deleteCount + Math.max(missingTypes, replaceCount);
    }
};

Solution in Java

Java
class Solution {
    public int strongPasswordChecker(String password) {
        int n = password.length();

        // Step 1: Check for the presence of lowercase, uppercase, and digit
        boolean hasLower = false, hasUpper = false, hasDigit = false;
        for (char c : password.toCharArray()) {
            if (Character.isLowerCase(c)) hasLower = true;
            if (Character.isUpperCase(c)) hasUpper = true;
            if (Character.isDigit(c)) hasDigit = true;
        }

        // Count missing character types
        int missingTypes = (hasLower ? 0 : 1) + (hasUpper ? 0 : 1) + (hasDigit ? 0 : 1);

        // Step 2: Count sequences of three or more repeating characters
        int replaceCount = 0; // Number of replacements to break repeating sequences
        int oneMod = 0, twoMod = 0; // Track lengths % 3 == 1 or 2
        for (int i = 2; i < n; ++i) {
            if (password.charAt(i) == password.charAt(i - 1) && password.charAt(i) == password.charAt(i - 2)) {
                int length = 2; // Start with 3 repeating characters
                while (i < n && password.charAt(i) == password.charAt(i - 1)) {
                    ++length;
                    ++i;
                }
                replaceCount += length / 3;
                if (length % 3 == 0) oneMod++;
                else if (length % 3 == 1) twoMod++;
            }
        }

        // Step 3: Handle different password lengths
        if (n < 6) {
            // If password is too short, we need to add characters
            return Math.max(6 - n, missingTypes);
        } else if (n <= 20) {
            // If password length is valid, replacements handle repeating characters
            return Math.max(missingTypes, replaceCount);
        } else {
            // If password is too long, we need to delete characters
            int deleteCount = n - 20;

            // Optimize replacements by using deletions
            replaceCount -= Math.min(deleteCount, oneMod); // Use 1 deletion for each group % 3 == 0
            replaceCount -= Math.min(Math.max(deleteCount - oneMod, 0), twoMod * 2) / 2; // Use 2 deletions for groups % 3 == 1
            replaceCount -= Math.max(deleteCount - oneMod - 2 * twoMod, 0) / 3; // Use 3 deletions for groups % 3 == 2

            return deleteCount + Math.max(missingTypes, replaceCount);
        }
    }
}

Subscribe
Notify of

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Popular