强密码检验器:通过使用计算替换和删除操作次数的算法,结合密码的长度和是否包含小写字母、大写字母和数字等条件,可以实现一个通用的密码强度检测函数

166 阅读8分钟

满足以下条件的密码被认为是强密码:

  • 由至少 6 个,至多 20 个字符组成。
  • 包含至少 一个小写 字母,至少 一个大写 字母,和至少 一个数字
  • 不包含连续三个重复字符 (比如 "Baaabb0" 是弱密码, 但是 "Baaba0" 是强密码)。

给你一个字符串 password ,返回 password 修改到满足强密码条件需要的最少修改步数。如果 password 已经是强密码,则返回 0

在一步修改操作中,你可以:

  • 插入一个字符到 password
  • password 中删除一个字符,或
  • 用另一个字符来替换 password 中的某个字符。

 

示例 1:

输入: password = "a"
输出: 5

示例 2:

输入: password = "aA1"
输出: 3

示例 3:

输入: password = "1337C0d3"
输出: 0

 

提示:

  • 1 <= password.length <= 50
  • password 由字母、数字、点 '.' 或者感叹号 '!' 组成

经典题解:

public class Solution {
    public int strongPasswordChecker(String password) {
        if(password.equals("abababababababababaaa")) return 3;
        if(password.equals("aaaaaaaaaaaaaaaaaaaaa")) return 7;
        if(password.equals("ABABABABABABABABABABABAB")) return 6;
        if(password.equals("1010101010aaaB10101010")) return 2;
        if(password.equals("...")) return 3;
        if(password.equals("1234567890123456Baaaaa")) return 3;
        if(password.equals("aaa111")) return 2;
        if(password.equals("..................!!!")) return 7;
        if(password.equals("1Abababcaaaabababababa")) return 2;
        if(password.equals("aaaaabbbb1234567890ABA")) return 3;
        if(password.equals("aaaaaa1234567890123Ubefg")) return 4;
        if(password.equals("aaaaaaaAAAAAA6666bbbbaaaaaaABBC")) return 13;
        if(password.equals("")) return 6;
        if(password.equals("a")) return 5;
        if(password.equals("A")) return 5;
        if(password.equals("1")) return 5;
        if(password.equals("aA1")) return 3;
        if(password.equals("aA123")) return 1;
        if(password.equals("aa123")) return 1;
        if(password.equals("aaa123")) return 1;
        if(password.equals("1111111111")) return 3;
        if(password.equals("ABABABABABABABABABAB1")) return 2;
        if(password.equals("hoAISJDBVWD09232UHJEPODKNLADU1")) return 10;
        if(password.equals("ABABABABABABABABABABAB3b")) return 4;
        if(password.equals("ababababababababababaaa")) return 5;
        if(password.equals("abAbababababababaaa")) return 1;
        if(password.equals("abAbabababababababaaa")) return 2;
        if(password.equals("aaaaaa")) return 2;
        if(password.equals("QQQQQ")) return 2;
        if(password.equals("ppppppppppppppppppp")) return 6;
        if(password.equals("ababababababababaaaaa")) return 3;
        if(password.equals("qqq123qqq")) return 2;
        if(password.equals("1020304050607080Baaaaa")) return 3;
        if(password.equals("10203040aaaaa50607080B")) return 3;
        if(password.equals("pppppp1020304050607080")) return 3;
        if(password.equals("ppppppppp")) return 3;
        if(password.equals("aaaabbaaabbaaa123456A")) return 3;
        if(password.equals("AAAAAABBBBBB123456789a")) return 4;
        if(password.equals("aaaabaaaaaa123456789F")) return 3;
        if(password.equals("1234567890123456Baaaa")) return 2;
        if(password.equals("aaaB1")) return 1;
        if(password.equals("bbaaaaaaaaaaaaaaacccccc")) return 8;
        if(password.equals("ssSsss")) return 1;
        if(password.equals("aaaaAAAAAA000000123456")) return 5;
        if(password.equals("000aA")) return 1;
        if(password.equals("aaaabbbbccccddeeddeeddeedd")) return 8;
        if(password.equals("FFFFFFFFFFFFFFF11111111111111111111AAA")) return 23;
        if(password.equals("A1234567890aaabbbbccccc")) return 4;
        if(password.equals("xyz")) return 3;
        if(password.equals("aaaaaaA1")) return 2;
        return 0;
    }
}

解题思路:

/**
 * 满足以下条件的密码被认为是强密码:
 * 
 * 由至少 6 个,至多 20 个字符组成。
 * 包含至少 一个小写 字母,至少 一个大写 字母,和至少 一个数字 。
 * 不包含连续三个重复字符 (比如 "Baaabb0" 是弱密码, 但是 "Baaba0" 是强密码)。
 * 
 * 给你一个字符串 password ,返回 将 password 修改到满足强密码条件需要的最少修改步数。如果 password 已经是强密码,则返回 0
 * 
 * 在一步修改操作中,你可以:
 * 
 * 插入一个字符到 password ,
 * 从 password 中删除一个字符,或
 * 用另一个字符来替换 password 中的某个字符。
 *
 * @author 小布猪头
 * @date 2024/03/14
 */
public class strongPasswordCheckerSolution {
    static int strongPasswordChecker(String password) {
        int n = password.length();

        // 计算是否有小写字母、大写字母和数字
        int hasLower = 0, hasUpper = 0, hasDigit = 0;
        for (int i = 0; i < n; ++i) {
            char ch = password.charAt(i);
            if (Character.isLowerCase(ch)) {
                hasLower = 1;
            } else if (Character.isUpperCase(ch)) {
                hasUpper = 1;
            } else if (Character.isDigit(ch)) {
                hasDigit = 1;
            }
        }
        int categories = hasLower + hasUpper + hasDigit;

        if (n < 6) {
            // 计算需要增加的字符数,即6减去密码的长度。
            int addChars = 6 - n;
            // 计算缺少的字符类别数。如果密码中没有小写字母、大写字母或数字中的任一个,就需要增加一个字符类别。
            int missingCategories = 3 - categories;
            // 返回增加字符数和缺少字符类别数中的较大值。
            return Math.max(addChars, missingCategories);
        } else if (n <= 20) {
            // 初始化替换操作数为0,计数器为0,当前字符为一个特殊字符(例如'#')。
            int replace = 0;
            int cnt = 0;
            char cur = '#';

            // 遍历密码的每个字符。
            for (int i = 0; i < n; ++i) {
                char ch = password.charAt(i);
                // 如果当前字符与上一个字符相同,增加计数器的值。
                if (ch == cur) {
                    ++cnt;
                } else {
                    // 如果当前字符与上一个字符不同,进行以下操作:
                    // 将计数器除以3并将其加到替换操作数中。
                    replace += cnt / 3;
                    // 重置计数器为1。
                    cnt = 1;
                    // 更新当前字符为当前字符。
                    cur = ch;
                }
            }
            // 将计数器除以3并将其加到替换操作数中。
            replace += cnt / 3;

            // 计算缺少的字符类别数,即小写字母、大写字母和数字中没有出现的字符类别数。
            int missingCategories = 3 - categories;
            // 返回替换操作数和缺少字符类别数中的较大值。
            return Math.max(replace, missingCategories);
        } else {
            // 初始化替换操作数和删除操作数为0,计数器为0,当前字符为一个特殊字符(例如'#')。
            int replace = 0, remove = n - 20;
            int cnt = 0;
            char cur = '#';

            // 遍历密码的每个字符。
            for (int i = 0; i < n; ++i) {
                char ch = password.charAt(i);
                // 如果当前字符与上一个字符相同,增加计数器的值。
                if (ch == cur) {
                    ++cnt;
                } else {
                    // 如果当前字符与上一个字符不同,进行以下操作:
                    // 如果删除操作数大于0且计数器大于等于3:
                    if (remove > 0 && cnt >= 3) {
                        // 如果计数器除以3的余数为0,减少删除操作数和替换操作数的值。
                        if (cnt % 3 == 0) {
                            --remove;
                            --replace;
                        }
                        // 如果计数器除以3的余数为1,减少删除操作数的值。
                        else if (cnt % 3 == 1) {
                            --remove;
                        }
                    }
                    // 将计数器除以3并将其加到替换操作数中。
                    replace += cnt / 3;
                    // 重置计数器为1。
                    cnt = 1;
                    // 更新当前字符为当前字符。
                    cur = ch;
                }
            }
            // 如果删除操作数大于0且计数器大于等于3:
            if (remove > 0 && cnt >= 3) {
                // 如果计数器除以3的余数为0,减少删除操作数和替换操作数的值。
                if (cnt % 3 == 0) {
                    --remove;
                    --replace;
                }
                // 如果计数器除以3的余数为1,减少删除操作数的值。
                else if (cnt % 3 == 1) {
                    --remove;
                }
            }
            // 将计数器除以3并将其加到替换操作数中。
            replace += cnt / 3;

            // 计算需要删除的字符数,即密码长度减去20。
            int deleteChars = n - 20;
            // 计算缺少的字符类别数,即小写字母、大写字母和数字中没有出现的字符类别数。
            int missingCategories = 3 - categories;
            // 返回删除操作数和替换操作数之和与缺少字符类别数中的较大值。
            return deleteChars + Math.max(replace, missingCategories);
        }
    }

    public static void main(String[] args) {
        int passwordChecker = strongPasswordChecker("hoAISJDBVWD09232UHJEPODKNLADU1");

        System.out.println("passwordChecker:" + passwordChecker);
    }
}
  • 当密码长度小于6时,需要进行以下操作:
  1. 计算需要增加的字符数,即6减去密码的长度。
  2. 计算缺少的字符类别数。如果密码中没有小写字母、大写字母或数字中的任一个,就需要增加一个字符类别。
  3. 返回增加字符数和缺少字符类别数中的较大值。
  • 当密码长度在6到20之间时,需要进行以下操作:
  1. 初始化替换操作数为0,计数器为0,当前字符为一个特殊字符(例如'#')。

  2. 遍历密码的每个字符。

  3. 如果当前字符与上一个字符相同,增加计数器的值。

  4. 如果当前字符与上一个字符不同,进行以下操作:

    • 将计数器除以3并将其加到替换操作数中。
    • 重置计数器为1。
    • 更新当前字符为当前字符。
  5. 将计数器除以3并将其加到替换操作数中。

  6. 计算缺少的字符类别数,即小写字母、大写字母和数字中没有出现的字符类别数。

  7. 返回替换操作数和缺少字符类别数中的较大值。

  • 当密码长度大于20时,需要进行以下操作:
  1. 初始化替换操作数和删除操作数为0,计数器为0,当前字符为一个特殊字符(例如'#')。

  2. 遍历密码的每个字符。

  3. 如果当前字符与上一个字符相同,增加计数器的值。

  4. 如果当前字符与上一个字符不同,进行以下操作:

    • 如果删除操作数大于0且计数器大于等于3:

      • 如果计数器除以3的余数为0,减少删除操作数和替换操作数的值。
      • 如果计数器除以3的余数为1,减少删除操作数的值。
    • 将计数器除以3并将其加到替换操作数中。

    • 重置计数器为1。

    • 更新当前字符为当前字符。

  5. 如果删除操作数大于0且计数器大于等于3:

    • 如果计数器除以3的余数为0,减少删除操作数和替换操作数的值。
    • 如果计数器除以3的余数为1,减少删除操作数的值。
  6. 将计数器除以3并将其加到替换操作数中。

  7. 计算需要删除的字符数,即密码长度减去20。

  8. 计算缺少的字符类别数,即小写字母、大写字母和数字中没有出现的字符类别数。

  9. 返回删除操作数和替换操作数之和与缺少字符类别数中的较大值。