【算法】Strong Password Checker 强密码检查

题目

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

It has at least 6 characters and at most 20 characters.
It must contain at least one lowercase letter, at least one uppercase letter, and at least one digit.
It must NOT contain three repeating characters in a row ("…aaa…" is weak, but “…aa…a…” is strong, assuming other conditions are met).
Write a function strongPasswordChecker(s), that takes a string s as input, and return the MINIMUM change required to make s a strong password. If s is already strong, return 0.

Insertion, deletion or replace of any one character are all considered as one change.

强密码检查,返回非强密码需要修改的最小操作步数,若是强密码返回 0,强密码需满足以下条件:

  1. 密码长度为 6 ~ 20
  2. 必须含有大小字母和数字
  3. 不能含有连续的三个及以上相同字符

解题思路

条件有三个,操作也有三个:
长度<6:insert
长度>20:delete
必须字符:insert、replace
重复问题:insert、replace、delete
由此可见,一个操作可以同时解决或缓解多个问题,要做的就是将操作价值最大化。
长度问题分别只能由insert和delete解决,必须字符问题insert和replace是等价的,操作步数相同,重复问题中操作价值是replace>=insert>=delete(replace n/3次,insert (n-1)/2次,delete n-2次)。
所以我们的逻辑就是,在解决长度<6时,由于情况简单,单独处理,在解决长度>20问题时,delete 过程中尽可能解决重复问题,在解决重复问题时,使用replace尽可能解决必须字符问题。

  1. 长度为 0 ,则返回 6
  2. 长度<6,解决长度问题操作范围为 1 ~ 5,解决重复问题操作范围为 1 ~ 2,解决必须字符问题操作范围为 1 ~ 2,三者取最大值返回即可
  3. 长度>20,
    • 在 delete 的同时,依次删除 n%3=0、1、2 (n为重复字符长度)的重复字符串 1、2、3 个字符串,如此便能在解决长度>20 问题同时最大效率解决重复字符问题
    • 若是依靠delete就能解决重复问题,由于delete 不能解决必须字符问题,所以删除字符长度+必须字符replace步数即为结果
    • 若是在解决了长度>20问题之后,仍旧有重复问题,则依据解决重复问题所需操作步数,加上delete步数,即为结果
  4. 重复问题,replace 操作在解决必须字符和重复问题的操作中都是最大价值的操作,所以取重复字符 replace 的总操作数,和必须字符 replace 操作数,取两者最大值返回

代码实现

func strongPasswordChecker(_ s: String) -> Int {
        //字符串长度len
        let len = s.count
        //若是长度为零,返回6
        if len==0 {
            return 6
        }
        //临时计算重复字符数的容器,长度为字符长度,值为1
        var repeatsTmp = [Int](repeating: 1, count: len)
        //记录重复字符数的容器,repeats[i][j],i 重复字符数余 3 ,范围是 0~2 ,对应数组的元素记录了num(重复字符数),iNum(解决重复问题需插入字符数),rNum(解决重复问题需替换字符数)。
        var repeats = [[[String:Int]](),[[String:Int]](),[[String:Int]]()]
        //大小写字母数字是否存在的表示
        var lower = true
        var upper = true
        var digit = true
        //必须字符类型默认为3
        var mustNum = 3
        // 解决重复问题一共需要插入的数量
        var totalINum = 0
        //记录前一个字符的表示
        var pre : Character? = nil
        //充当s中char的index
        var index = 0
        for ch in s {
            //判断是否为大小写字母和数字,若是,则 mustNum - 1
            if(ch.isLowercase && lower){
                lower = false
                mustNum -= 1
            }else if(ch.isUppercase && upper){
                upper = false
                mustNum -= 1
            }else if(ch.isNumber && digit){
                digit = false
                mustNum -= 1
            }
            //如果当前字符与前一个字符相等,则为重复字符,将repeatsTmp当前对应的值加的前一个位置的值,并重置前一个位置的值为1,只记录该重复字符最终最大值
            if pre == ch {
                repeatsTmp[index] = 1 + repeatsTmp[index-1]
                repeatsTmp[index-1] = 1
            }else if (index>0 && repeatsTmp[index-1]>2){
                //若当前字符与前一个字符不相等,且非首字符,前一个重复值大于2,说明前一个重复字符是需要处理的重复字符,加入到repeats对应位置中
                //n 为重复字符数量
                let n = repeatsTmp[index-1]
                //m 为 n%3 作为 repeats 中对应位置
                let m = n % 3
                //用 configRepeatChar 创建对应元素
                let el = self.configRepeatChar(num: n)
                // 记录总共需要插入的数量
                totalINum += el["iNum"]!
                //将元素加入到repeats对应位置
                repeats[m].append(el)
            }
            //赋值pre和index
            pre = ch
            index += 1
        }
        //循环结束,判断最后一段字符是不是重复字符
        if index==len && repeatsTmp[index-1]>2 {
            let n = repeatsTmp[index-1]
            let m = n % 3
            let el = self.configRepeatChar(num: n)
            totalINum += el["iNum"]!
            repeats[m].append(el)
        }
        //设置解决问题需要的步数,初始化为0
        var steps = 0
        if len<6 {
            //当长度小于6时,解决长度问题需要插入字符操作为1~5步,解决必须字符种类的操作为1~2步,解决重复字符串问题操作为1~2步
            //取三者最大值即可
            let insertNum = 6-len
            steps = max(max(insertNum, mustNum), totalINum)
            return steps
        }else if (len>20){
            //当长度大于20时,deleNum 为需要删除的长度
            var deleNum = len-20
            //以重复长度余3的结果为 0、1、2 的顺序去执行删除 1、2、3个字符的操作
            //因为如此会以最少的删除操作在解决长度问题的同时,尽可能解决重复字符串的问题
            for index in 0 ..< repeats.count {
                //充当index
                var elIn = 0
                for el in repeats[index] {
                    //本次循环可以删除的长度
                    var deleSteps = index + 1
                    //与还需删除的总长度比较,取较小值,防止删除过度
                    deleSteps = deleNum>deleSteps ? deleSteps : deleNum
                    //还需删除的总长度,减去本次删除的长度
                    deleNum -= deleSteps
                    //重复字符长度减去删除长度得到新长度 n
                    let num = el["num"]! - deleSteps
                    let m = num % 3
                    //用 configRepeatChar 创造 n 的新元素,并加入到 repeats 对应位置中
                    let elNew = self.configRepeatChar(num: num)
                    repeats[m].append(elNew)
                    //删除原来的元素
                    let ind = repeats[index].firstIndex(of: el)!
                    repeats[index].remove(at: ind)
                    //如果需删除的总长度为 0 ,则说明长度复合要求,跳出循环
                    if deleNum==0 {
                        break
                    }
                    elIn += 1
                }
                //如果需删除的总长度为 0 ,则说明长度复合要求,跳出循环
                if deleNum==0 {
                    break
                }
            }
            //如果需删除的总长度大于 0 ,则说明重复问题解决后,还需要继续删除字符以解决长度问题,则返回结果为总共需要删除的长度加上mustNum
            if deleNum>0 {
                return len-20+mustNum
            }else{
                //否则,则说明在重复问题解决或解决前,长度问题解决,操作步数则加上需删除的总长度
                steps += len-20
            }
        }
        
        //此时,字符串的长度符合要求,遍历 repeats 取出解决重复字符串总共需要替换的操作步数 rNum
        var rNum = 0
        for els in repeats {
            for el in els {
                rNum += el["rNum"]!
            }
        }
        //rNum 和 mustNum 取较大值,加到总操作步数里
        steps += rNum>mustNum ? rNum : mustNum
        //返回总操作步数
        return steps
    }
    func configRepeatChar(num: Int) -> [String:Int]{
        let m = (num-1)/2
        let n = num/3
        return ["num":num, "iNum":m, "rNum":n]
    }

代码地址:https://github.com/sinianshou/EGSwiftLearning

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的 Java 代码示例,用于校验用户输入的密码是否为弱密码,并为用户提供一个密码: ```java import java.util.Random; import java.util.Scanner; public class PasswordChecker { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入密码:"); String password = scanner.next(); if (isWeak(password)) { System.out.println("密码过于简单,请重新输入!"); password = generateStrongPassword(); System.out.println("您的新密码为:" + password); } else { System.out.println("密码校验通过!"); } } // 判断是否为弱密码 private static boolean isWeak(String password) { // 密码长度小于8位或只包含数字或字母或与用户名相同 return password.length() < 8 || password.matches("[0-9]+") || password.matches("[a-zA-Z]+") || password.equals("username"); } // 生成密码 private static String generateStrongPassword() { String upperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; String lowerCaseLetters = "abcdefghijklmnopqrstuvwxyz"; String numbers = "0123456789"; String specialChars = "!@#$%^&*()_+-="; String allChars = upperCaseLetters + lowerCaseLetters + numbers + specialChars; Random random = new Random(); StringBuilder password = new StringBuilder(); // 生成长度为12的密码 for (int i = 0; i < 12; i++) { int index = random.nextInt(allChars.length()); password.append(allChars.charAt(index)); } return password.toString(); } } ``` 以上代码示例中,我们通过 `isWeak` 方法判断用户输入的密码是否为弱密码,如果是,则调用 `generateStrongPassword` 方法生成一个密码,并返回给用户。在 `generateStrongPassword` 方法中,我们定义了一些包含大写字母、小写字母、数字、特殊字符的字符集,然后随机生成一个长度为12的密码。需要注意的是,为了保证密码度和随机性,我们在生成密码时使用了 `Random` 类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值