LeetCode 728. 自除数 / 954. 二倍数对数组 / 420. 强密码检验器

本文涵盖了关于自除数的定义和实现,以及数组操作的两种问题。自除数是指不含0且能被其每一位整除的数字。示例中展示了如何在给定范围内找出所有自除数。此外,文章还讨论了如何重组数组使得每对连续元素满足两倍关系,以及检查一个密码是否满足强密码条件所需的最少修改次数。这些问题涉及数组排序、条件判断和字符计数等算法技巧。
摘要由CSDN通过智能技术生成

728. 自除数

2022.3.31 每日一题

题目描述

自除数 是指可以被它包含的每一位数整除的数。

  • 例如,128 是一个 自除数 ,因为 128 % 1 == 0,128 % 2 == 0,128 % 8 == 0。
    自除数 不允许包含 0 。

给定两个整数 left 和 right ,返回一个列表,列表的元素是范围 [left, right] 内所有的 自除数 。

示例 1:

输入:left = 1, right = 22
输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]

示例 2:

输入:left = 47, right = 85
输出:[48,55,66,77]

提示:

1 <= left <= right <= 10^4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/self-dividing-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

java

class Solution {
    public List<Integer> selfDividingNumbers(int left, int right) {
        //有什么好的办法吗
        List<Integer> list = new ArrayList<>();
        for(int i = left; i <= right; i++){
            int num = i;
            boolean flag = true;
            while(num != 0){
                int t = num % 10;
                if(t == 0 || i % t != 0){
                    flag = false;
                    break;
                }
                num /= 10;
            }
            if(flag)
                list.add(i);
        }
        return list;
    }
}

python

class Solution:
    def selfDividingNumbers(self, left: int, right: int) -> List[int]:
        res = []
        for i in range(left, right + 1):
            temp = i
            flag = True
            # 如果temp等于0
            while temp > 0:
                t = temp % 10
                if t == 0 or i % t != 0:
                    flag = False
                    break
                temp //= 10
            if flag:
                res.append(i)
        return res 

954. 二倍数对数组

2022.4.1 每日一题

题目描述

给定一个长度为偶数的整数数组 arr,只有对 arr 进行重组后可以满足 “对于每个 0 <= i < len(arr) / 2,都有 arr[2 * i + 1] = 2 * arr[2 * i]” 时,返回 true;否则,返回 false。

示例 1:

输入:arr = [3,1,3,6]
输出:false

示例 2:

输入:arr = [2,1,2,6]
输出:false

示例 3:

输入:arr = [4,-2,2,-4]
输出:true
解释:可以用 [-2,-4] 和 [2,4] 这两组组成 [-2,-4,2,4] 或是 [2,4,-2,-4]

提示:

0 <= arr.length <= 3 * 10^4
arr.length 是偶数
-10^5 <= arr[i] <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/array-of-doubled-pairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution {
    int mod = (int)1e6;
    public boolean canReorderDoubled(int[] arr) {
        //就是什么呢,就是有len/2组两倍关系的数对
        //直接排序,然后从小到大找这种关系,如果都能找到,那么true

        int l = arr.length;
        Arrays.sort(arr);
        Map<Integer, List<Integer>> map = new HashMap<>();
        for(int i = 0; i < l; i++){
            int t = arr[i];
            List<Integer> list = map.getOrDefault(t, new ArrayList<>());
            list.add(i);
            map.put(t, list);
        }

        //0拿出来单独判断,如果有偶数个0跳过
        List<Integer> zero = map.getOrDefault(0, new ArrayList<>());
        if(zero.size() % 2 == 1)
            return false;

        for(int i = 0; i < l; i++){
            int t = arr[i];
            if(t == mod || t == 0)
                continue;
    
            int need = 0;
            if(t >= 0){
                need = t * 2;
            }else{
                if(t % 2 != 0)
                    return false;
                need = t / 2;
            }
            if(!map.containsKey(need)){
                return false;
            }
            List<Integer> list = map.get(need);
            int idx = list.get(0);
            list.remove(0);
            arr[idx] = mod;
            if(list.isEmpty())
                map.remove(need);
            else
                map.put(need, list);

        }
        return true;
    }
}

其实直接成组的判断就可以了
例如,有5个1,那么必须就有5个2这种
这里不需要特殊判断0

class Solution {
    static int N = 100010, M = N * 2;
    static int[] cnts = new int[M * 2];
    public boolean canReorderDoubled(int[] arr) {
        Arrays.fill(cnts, 0);
        List<Integer> list = new ArrayList<>();
        for (int i : arr) {
            if (++cnts[i + M] == 1) list.add(i);
        }
        Collections.sort(list, (a,b)->Math.abs(a)-Math.abs(b));
        for (int i : list) {
            if (cnts[i * 2 + M] < cnts[i + M]) return false;
            cnts[i * 2 + M] -= cnts[i + M];
        }
        return true;
    }
}

420. 强密码检验器

2022.4.2 每日一题

题目描述

如果一个密码满足下述所有条件,则认为这个密码是强密码:

  • 由至少 6 个,至多 20 个字符组成。
  • 至少包含 一个小写 字母,一个大写 字母,和 一个数字 。
  • 同一字符 不能 连续出现三次 (比如 “…aaa…” 是不允许的, 但是 “…aa…a…” 如果满足其他条件也可以算是强密码)。

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

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

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

示例 1:

输入:password = “a”
输出:5

示例 2:

输入:password = “aA1”
输出:3

示例 3:

输入:password = “1337C0d3”
输出:0

提示:

1 <= password.length <= 50
password 由字母、数字、点 ‘.’ 或者感叹号 ‘!’

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/strong-password-checker
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

做了半天,写了老长,根据长度分三种情况
如果小于6,那么添加优先
如果满足长度条件,替换就可以了
如果大于20,那么肯定需要删除len-20个字符,但是删除哪个字符是有讲究的,因为删除会影响替换次数
例如有六个连续的字符,那么删除1个就可以减少1次替换,也就是余数为0的情况
同理,余数为1,删除两个可以减少替换次数
所以需要统计连续字符的个数,然后先删除余数为0的,再删除余数为1的…

我这里用map记录的,可以看看题解的代码,有很简洁的,这,任重道远

class Solution {
    public int strongPasswordChecker(String password) {
        //假设当前密码中包含的字符种类有t个
        //如果小于6个,那么肯定是添加操作优先,如果是好多个相同的字符,那么还需要替换
        //需要的操作数就是max(6 - len, 3 - t)

        //如果在20个以内,那么肯定是替换操作优先,就需要看是否有连续三个字符,如果出现了连续三个,直接替换
        //需要的操作数就是连续字符的个数,如果没有连续的字符但是种类不足,那只需要随便替换两个就行了

        //如果是20个以上,那么就是删除操作优先,需要删除的数目就是len - 20
        //所以当出现了连续的字符时,需要优先考虑删除操作
        //如果删除以后还是相同,那么再考虑替换操作

        //"bbaaaaaaaaaaaaaaacccccc"这个例子出错了,如果先删除后替换,那么删除的是3个a,然后替换需要替换4个a两个c
        //但是实际上,删除两个a一个c,那么替换的时候,替换1个c,4个a就可以了
        //所以这给了个什么启发呢,为了让替换次数最少,需要看情况删除
        //如果连续的字符除以3余数为0,那么可以删除1个,从而使替换次数少1
        //如果余数为1,那么删除两个,使替换次数少1
        //如果余数为2,那么删除三个,使替换次数少1
        //当然,前提是删除次数还有的时候
        //所以第一步需要先统计字符串中连续字符的个数,先删除余数为0的,然后1的,然后2的
        //如果删除次数不够了,就开始替换
        //如果删除完以后,依然有次数,那么就直接对连续的进行删除就行了,因为都是余数为2

        int[] type = new int[3];
        int l = password.length();
        //System.out.println(l);
        for(char c : password.toCharArray()){
            if(Character.isDigit(c))
                type[0] = 1;
            else if(Character.isLowerCase(c))
                type[1] = 1;
            else if(Character.isUpperCase(c))
                type[2] = 1;
        }
        int kind = 0;
        for(int t : type)
            kind += t;

        if(l < 6){
            return Math.max(6 - l, 3 - kind);
        }else if(l <= 20){
            int continuous = 0;
            char temp = ' ';
            int modify = 0;
            for(char c : password.toCharArray()){
                if(temp != c){
                    //如果有连续三个的就替换一个
                    modify += continuous / 3;
                    temp = c;
                    continuous = 1;
                }else{
                    continuous++;
                }
            }
            modify += continuous / 3;
            return Math.max(modify, 3 - kind);
        }else{
            //当大于20个时,肯定是优先删除,如果删除到只剩下20了,再和上面一样进行替换
            //需要删除的数目
            int del = l - 20;
            char temp = ' ';
            Map<Integer, List<Integer>> map = new HashMap<>();
            int continuous = 0;
            for(char c : password.toCharArray()){
                if(temp != c){
                    int remain = continuous % 3;
                    if(continuous >= 3){
                        List<Integer> list = map.getOrDefault(remain, new ArrayList<>());
                        list.add(continuous);
                        map.put(remain, list);
                    }
                    temp = c;
                    continuous = 1;
                    
                }else{
                    continuous++;
                }
            }
            int remain = continuous % 3;
            if(continuous >= 3){
                List<Integer> list = map.getOrDefault(remain, new ArrayList<>());
                list.add(continuous);
                map.put(remain, list);
            }
            //为了能使替换次数减少,先删除余数为0的
            List<Integer> zero = map.getOrDefault(0, new ArrayList<>());
            List<Integer> two = map.getOrDefault(2, new ArrayList<>());

            while(del > 0 && !zero.isEmpty()){
                int t = zero.remove(0);
                t--;
                del--;
                if(t >= 3)
                    two.add(t);
            }
            List<Integer> one = map.getOrDefault(1, new ArrayList<>());

            while(del > 1 && !one.isEmpty()){
                int t = one.remove(0);
                t -= 2;
                del -= 2;
                if(t >= 3)
                    two.add(t);
            }
            //System.out.println(zero.size());
            //System.out.println(one.size());
            //到这里,剩下的连续的都是余数为2或者说删除次数还多
            //如果删除次数还多,那么直接在任意连续的地方进行删除,因为不过改变替换次数
            //另外,如果删除次数还多,那么只有two这个list有数
            //那么就需要进行替换了
            int replace = 0;
            for(int t : zero){
                replace += t / 3;
            }
            for(int t : one){
                replace += t / 3;
            }
            for(int t : two){
                if(del > 0){
                    int jian = Math.min(del, t - 2);
                    t -= jian;
                    del -= jian;
                }
                replace += t / 3;
            }
            return l - 20 + Math.max(replace, 3 - kind);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值