欢迎大家加QQ群:623375442,可以方便群里面交流。

100394. Count Almost Equal Pairs I

给你一个正整数数组 nums 。

如果我们执行以下操作 至多一次 可以让两个整数 x 和 y 相等,那么我们称这个数对是 近似相等 的:

  • 选择 x 或者 y 之一,将这个数字中的两个数位交换。

请你返回 nums 中,下标 i 和 j 满足 i < j 且 nums[i] 和 nums[j] 近似相等 的数对数目。

注意 ,执行操作后一个整数可以有前导 0 。

测试样例:

输入:nums = [3,12,30,17,21]

输出:2

解释:近似相等数对包括:

  • 3 和 30 。交换 30 中的数位 3 和 0 ,得到 3 。
  • 12 和 21 。交换12 中的数位 1 和 2 ,得到 21 。

解答:这题范围不大,我们之间暴力两重循环,然后暴力计算这两个数是否相似。

class Solution {
    public int countPairs(int[] nums) {
        int res = 0;
        for (int i = 0; i < nums.length; ++i) {
            for (int j = i + 1; j < nums.length; ++j) {
                if (nums[i] == nums[j] || distanceOne(nums[i], nums[j])) {
                    ++res;
                }
            }
        }
        return res;
    }

    private boolean distanceOne(int n1, int n2) {
        if (n1 > n2) {
            return distanceOne(n2, n1);
        }
        String str1 = String.valueOf(n1), str2 = String.valueOf(n2);
        while (str1.length() != str2.length()) {
            str1 = '0' + str1;
        }
        int firstMis = -1;
        boolean isUsed = false;
        for (int i = 0; i < str1.length(); ++i) {
            if (str1.charAt(i) != str2.charAt(i)) {
                if (isUsed) {
                    return false;
                } else if (firstMis == -1) {
                    firstMis = i;
                } else if (str1.charAt(firstMis) == str2.charAt(i) && str1.charAt(i) == str2.charAt(firstMis)) {
                    firstMis = -1;
                    isUsed = true;
                } else {
                    return false;
                }
            }
        }
        return firstMis == -1;
    }
}

100412. Final Array State After K Multiplication Operations II

给你一个整数数组 nums ,一个整数 k 和一个整数 multiplier 。

你需要对 nums 执行 k 次操作,每次操作中:

  • 找到 nums 中的 最小 值 x ,如果存在多个最小值,选择最 前面 的一个。
  • 将 x 替换为 x * multiplier 。

k 次操作以后,你需要将 nums 中每一个数值对 10^9 + 7 取余。

请你返回执行完 k 次乘运算以及取余运算之后,最终的 nums 数组。

测试样例:

输入:nums = [2,1,3,5,6], k = 5, multiplier = 2

输出:[8,4,6,5,6]

解释:
操作 结果

  • 1 次操作后 [2, 2, 3, 5, 6]
  • 2 次操作后 [4, 2, 3, 5, 6]
  • 3 次操作后 [4, 4, 3, 5, 6]
  • 4 次操作后 [4, 4, 6, 5, 6]
  • 5 次操作后 [8, 4, 6, 5, 6]

取余操作后 [8, 4, 6, 5, 6]

解答:首先需要注意multiplier = 1的情况。我TLE了2次,一直以为是算法复杂度不对。

首先我们先暴力计算一下,让整个数组的最大和最小相除小于multiplier。这样,每次循环就是对这个数组按大小遍历一次。根据取余数的情况,平铺一下操作。

class Solution {
    private static final int mod = 1_000_000_007;

    public int[] getFinalState(int[] nums, int k, int multiplier) {
        if (multiplier == 1) {
            return nums;
        }
        long[] dup = new long[nums.length];
        TreeSet<Integer> set = new TreeSet<>((a, b) -> (dup[a] != dup[b] ? Long.compare(dup[a], dup[b]) : Long.compare(a, b)));
        for (int i = 0; i < dup.length; ++i) {
            dup[i] = nums[i];
            set.add(i);
        }
        // 让整个数组的最大和最小相除小于multiplier。比如[2,1,3,5,6] -> [4, 4, 6, 5, 6]。之后,就是按照大小按顺序一个个乘multilpler
        while (dup[set.first()] * multiplier <= dup[set.last()] && k > 0) {
            int f = set.pollFirst();
            dup[f] *= multiplier;
            set.add(f);
            --k;
        }
        if (k == 0) {
            return turnArr(dup);
        } else {
            // 平均分配次数
            int time = k / nums.length, remain = k % nums.length;
            int count = 0;
            HashMap<Integer, Long> mem = new HashMap<>();
            for (int i : set) {
                int curTime = time;
                if (count < remain) {
                    curTime += 1;
                }
                dup[i] = (dup[i] % mod) * mul(multiplier, curTime, mem) % mod;
                ++count;
            }
            return turnArr(dup);
        }
    }

    private int[] turnArr(long[] dup) {
        int[] res = new int[dup.length];
        for (int i = 0; i < res.length; ++i) {
            res[i] = (int) (dup[i] % mod);
        }
        return res;
    }

    private long mul(int mul, int time, HashMap<Integer, Long> mem) {
        if (time == 0) {
            return 1;
        } else if (!mem.containsKey(time)) {
            long tmp = mul(mul, time / 2, mem);
            tmp = (tmp * tmp) % mod;
            if (time % 2 == 1) {
                tmp = (tmp * mul) % mod;
            }
            mem.put(time, tmp);
        }
        return mem.get(time);
    }
}

100403. Count Almost Equal Pairs II

注意:在这个问题中,操作次数增加为至多 两次 。

给你一个正整数数组 nums 。

如果我们执行以下操作 至多两次 可以让两个整数 x 和 y 相等,那么我们称这个数对是 近似相等 的:

  • 选择 x 或者 y 之一,将这个数字中的两个数位交换。

请你返回 nums 中,下标 i 和 j 满足 i < j 且 nums[i] 和 nums[j] 近似相等 的数对数目。

注意 ,执行操作后得到的整数可以有前导 0 。

注意,该整数 不 含前导零。

测试样例:

输入:nums = [1023,2310,2130,213]

输出:4

解释:近似相等数对包括:

  • 1023 和 2310 。交换 1023 中数位 1 和 2 ,然后交换数位 0 和 3 ,得到 2310 。
  • 1023 和 213 。交换 1023 中数位 1 和 0 ,然后交换数位 1 和 2 ,得到 0213 ,也就是 213 。
  • 2310 和 213 。交换 2310 中数位 2 和 0 ,然后交换数位 3 和 2 ,得到 0213 ,也就是 213 。
  • 2310 和 2130 。交换 2310 中数位 3 和 1 ,得到 2130 。

解答:本来以为这题可以用第二题的算法暴力过,没想到会超时。那就反向思维一下,我们遍历数组。将每个数的任意2组数字互换,查看能有多少hit到的次数。

class Solution {
    public int countPairs(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int n : nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }
        int res = 0;
        for (int n : nums) {
            int[] arr = trans(n);
            Set<Integer> set = new HashSet<>();
            map.put(n, map.get(n) - 1);
            for (int a = 0; a < 8; ++a) {
                for (int b = a + 1; b < 8; ++b) {
                    // 任意2位互换一次
                    swap(arr, a, b);
                    {
                        int num = trans(arr);
                        if (!set.contains(num)) {
                            set.add(num);
                            res += map.getOrDefault(num, 0);
                        }
                    }
                    for (int c = 0; c < 8; ++c) {
                        for (int d = c + 1; d < 8; ++d) {
                            // 任意2位再互换一次
                            swap(arr, c, d);
                            {
                                int num = trans(arr);
                                if (!set.contains(num)) {
                                    set.add(num);
                                    res += map.getOrDefault(num, 0);
                                }
                            }
                            swap(arr, c, d);
                        }
                    }
                    swap(arr, a, b);
                }
            }
        }
        return res;
    }

    private int[] trans(int n) {
        int[] arr = new int[8];
        for (int i = 7; i >= 0; --i) {
            arr[i] = n % 10;
            n /= 10;
        }
        return arr;
    }

    private int trans(int[] n) {
        int res = 0;
        for (int i = 0; i < n.length; ++i) {
            res = res * 10 + n[i];
        }
        return res;
    }

    private void swap(int[] arr, int x, int y) {
        int tmp = arr[y];
        arr[y] = arr[x];
        arr[x] = tmp;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *