欢迎大家加QQ群:623375442,可以方便群里面交流。第二题是第四题的一种特殊情况。我就单独放第四题题解了。

100443. Find the Maximum Factor Score of Array

给你一个整数数组 nums。

因子得分 定义为数组所有元素的最小公倍数(LCM)与最大公约数(GCD)的 乘积。

在 最多 移除一个元素的情况下,返回 nums 的 最大因子得分。

注意,单个数字的 LCM 和 GCD 都是其本身,而 空数组 的因子得分为 0。

lcm(a, b) 表示 a 和 b 的 最小公倍数。

gcd(a, b) 表示 a 和 b 的 最大公约数。

测试样例:

输入: nums = [2,4,8,16]

输出: 64

解释:移除数字 2 后,剩余元素的 GCD 为 4,LCM 为 16,因此最大因子得分为 4 * 16 = 64。

解答:这题暴力枚举lcm和gcd。

class Solution {
    public long maxScore(int[] nums) {
        long res = removeOneElement(nums, -1);
        for (int i = 0; i < nums.length; ++i) {
            res = Math.max(res, removeOneElement(nums, i));
        }
        return res;
    }

    private long removeOneElement(int[] nums, int t) {
        long gcd = -1, lcm = -1;
        for (int i = 0; i < nums.length; ++i) {
            if (i != t) {
                if (gcd == -1) {
                    gcd = nums[i];
                    lcm = nums[i];
                } else {
                    gcd = gcd(gcd, nums[i]);
                    lcm = lcm * nums[i] / gcd(lcm, nums[i]);
                }
            }
        }
        return gcd * lcm;
    }

    private long gcd(long x, long y) {
        while (y != 0) {
            long tmp = x % y;
            x = y;
            y = tmp;
        }
        return x;
    }
}

100454. Find the Number of Subsequences With Equal GCD

给你一个整数数组 nums。

请你统计所有满足一下条件的 非空 子序列对 (seq1, seq2) 的数量:

  • 子序列 seq1 和 seq2 不相交,意味着 nums 中 不存在 同时出现在两个序列中的下标。
  • seq1 元素的 GCD 等于 seq2 元素的 GCD。

返回满足条件的子序列对的总数。

由于答案可能非常大,请返回其对 10^9 + 7 取余 的结果。

gcd(a, b) 表示 a 和 b 的 最大公约数。

子序列 是指可以从另一个数组中删除某些或不删除元素得到的数组,并且删除操作不改变其余元素的顺序。

测试样例:

输入:nums = [1,2,3,4]

输出:10

解答:这题有点脑经急转弯。注意提示的范围1 <= nums.length <= 200,1 <= nums[i] <= 200。这样猜测一下,我们设定dp的内容是第i个位置,且到达它之前左右两个子序列的gcd值。这样复杂度应该可控。

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

    public int subsequencePairCount(int[] nums) {
        // dp为达到第i个位置,两个子序列的当前GCD值
        Integer[][][] dp = new Integer[201][201][nums.length];
        // 第一个和第二个0代表子序列中一个数字都没有
        return helper(0, 0, 0, nums, dp);
    }

    private int helper(int l, int r, int pos, int[] nums, Integer[][][] dp) {
        if (pos == nums.length) {
            if (l != 0 && r != 0 && l == r) {
                return 1;
            } else {
                return 0;
            }
        } else if (dp[l][r][pos] == null) {
            int res = 0;
            // 当前数字不加入子序列
            res  = (res + helper(l, r, pos + 1, nums, dp)) % mod;
            // 当前数字加入左子序列
            if (l == 0) {
                res = (res + helper(nums[pos], r, pos + 1, nums, dp)) % mod;
            } else {
                res = (res + helper(gcd(l, nums[pos]), r, pos + 1, nums, dp)) % mod;
            }
            // 当前数字加入右子序列
            if (r == 0) {
                res = (res + helper(l, nums[pos], pos + 1, nums, dp)) % mod;
            } else {
                res = (res + helper(l, gcd(nums[pos], r), pos + 1, nums, dp)) % mod;
            }
            dp[l][r][pos] = res;
        }
        return dp[l][r][pos];
    }

    private int gcd(int x, int y) {
        while (y != 0) {
            int tmp = x % y;
            x = y;
            y = tmp;
        }
        return x;
    }
}

100472. Total Characters in String After Transformations II

给你一个由小写英文字母组成的字符串 s,一个整数 t 表示要执行的 转换 次数,以及一个长度为 26 的数组 nums。每次 转换 需要根据以下规则替换字符串 s 中的每个字符:

  • 将 s[i] 替换为字母表中后续的 nums[s[i] - 'a'] 个连续字符。例如,如果 s[i] = 'a' 且 nums[0] = 3,则字符 'a' 转换为它后面的 3 个连续字符,结果为 "bcd"。
  • 如果转换超过了 'z',则 回绕 到字母表的开头。例如,如果 s[i] = 'y' 且 nums[24] = 3,则字符 'y' 转换为它后面的 3 个连续字符,结果为 "zab"。

返回 恰好 执行 t 次转换后得到的字符串的 长度。

由于答案可能非常大,返回其对 10……9 + 7 取余的结果。

测试样例:

输入:s = "abcyy", t = 2, nums = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2]

输出:7

解释:

  • 第一次转换 (t = 1)
    • 'a' 变为 'b' 因为 nums[0] == 1
    • ''b' 变为 'c' 因为 nums[1] == 1
    • ''c' 变为 'd' 因为 nums[2] == 1
    • ''y' 变为 'z' 因为 nums[24] == 1
    • ''y' 变为 'z' 因为 nums[24] == 1
    • '第一次转换后的字符串为: "bcdzz"
  • '第二次转换 (t = 2)
    • 'b' 变为 'c' 因为 nums[1] == 1
    • 'c' 变为 'd' 因为 nums[2] == 1
    • 'd' 变为 'e' 因为 nums[3] == 1
    • 'z' 变为 'ab' 因为 nums[25] == 2
    • 'z' 变为 'ab' 因为 nums[25] == 2
    • 第二次转换后的字符串为: "cdeabab"
  • 字符串最终长度: 字符串为 "cdeabab",长度为 7 个字符。

解答:这题t的范围实在太大了,没办法暴力枚举。想了一下,可以用折半的办法计算。大概思路就是假设当前字符是a,我计算一下最近2^k次方的变化,然后先跳跃一下。依此类推

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

    private class InternalData {
        private TreeMap<Integer, int[][]> skipTransform;

        public InternalData(List<Integer> nums) {
            int[][] last = new int[26][26];
            skipTransform = new TreeMap<>();
            int offset = 0;
            for (int n : nums) {
                for (int i = 0; i < n; ++i) {
                    last[offset][(offset + i + 1) % 26] += 1;
                }
                ++offset;
            }
            skipTransform.put(1, last);
            // 计算所有2^n次数的跳跃情况
            for (int i = 2; i < Integer.MAX_VALUE / 2; i = i * 2) {
                int[][] tmp = new int[26][26];
                for (int c = 0; c < 26; ++c) {
                    for (int t1 = 0; t1 < 26; ++t1) {
                        if (last[c][t1] > 0) {
                            long add = last[c][t1];
                            for (int t2 = 0; t2 < 26; ++t2) {
                                long mul = (add * last[t1][t2]) % mod;
                                tmp[c][t2] = (int) ((tmp[c][t2] + mul) % mod);
                            }
                        }
                    }
                }
                skipTransform.put(i, tmp);
                last = tmp;
            }
        }

        public Map.Entry<Integer, int[][]> floor(int key) {
            return skipTransform.floorEntry(key);
        }
    }

    public int lengthAfterTransformations(String s, int t, List<Integer> nums) {
        InternalData data = new InternalData(nums);
        int res = 0;
        int[] mem = new int[26];
        Arrays.fill(mem, -1);
        for (int i = 0; i < s.length(); ++i) {
            if (mem[s.charAt(i) - 'a'] == -1) {
                mem[s.charAt(i) - 'a'] = getResult(s.charAt(i), t, data);
            }
            res = (res + mem[s.charAt(i) - 'a']) % mod;
        }
        return res;
    }

    // 折半搜索
    private int getResult(char c, int t, InternalData data) {
        int[] add = new int[26];
        add[c - 'a'] += 1;
        while (t > 0) {
            int[] nextAdd = new int[26];
            Map.Entry<Integer, int[][]> floor = data.floor(t);
            for (int i = 0; i < 26; ++i) {
                if (add[i] > 0) {
                    long tmp = add[i];
                    for (int j = 0; j < 26; ++j) {
                        long mul = tmp * floor.getValue()[i][j] % mod;
                        nextAdd[j] = (int) ((nextAdd[j] + mul) % mod);
                    }
                }
            }
            t -= floor.getKey();
            add = nextAdd;
        }
        int res = 0;
        for (int n : add) {
            res = (res + n) % mod;
        }
        return res;
    }
}

Leave a Reply

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