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

100284. Valid Word

有效单词 需要满足以下几个条件:

  • 至少 包含 3 个字符。
  • 由数字 0-9 和英文大小写字母组成。(不必包含所有这类字符。)
  • 至少 包含一个 元音字母 。
  • 至少 包含一个 辅音字母 。

给你一个字符串 word 。如果 word 是一个有效单词,则返回 true ,否则返回 false 。

注意:

  • 'a'、'e'、'i'、'o'、'u' 及其大写形式都属于 元音字母 。
  • 英文中的 辅音字母 是指那些除元音字母之外的字母。

测试样例:

输入:word = "234Adas"

输出:true

解释:这个单词满足所有条件。

解答:按照题意分部判断条件是否满足。

class Solution {
    private static final char[] vowels = {'a','e','i','o','u'};

    public boolean isValid(String word) {
        // 长度大于3
        if (word.length() < 3) {
            return false;
        }
        boolean hasVowel = false, hasConsonant = false;
        for (int i = 0; i < word.length(); ++i) {
            char c = word.charAt(i);
            // 只能是数字或者英语大小写
            if (c >= '0' && c <= '9') {
                continue;
            } else if (isEnglish(c)) {
                // 记录元音字母 和 辅音字母情况
                if (isVowel(c)) {
                    hasVowel = true;
                } else {
                    hasConsonant = true;
                }
            } else {
                return false;
            }
        }
        // 判断同时存在元音字母 和 辅音字母
        return hasVowel && hasConsonant;
    }

    private boolean isVowel(char c) {
        for (char t : vowels) {
            if (c == t || c == (t - 'a' + 'A')) {
                return true;
            }
        }
        return false;
    }

    private boolean isEnglish(char c) {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    }
}

100275. Minimum Number of Operations to Make Word K-Periodic

给你一个长度为 n 的字符串 word 和一个整数 k ,其中 k 是 n 的因数。

在一次操作中,你可以选择任意两个下标 i 和 j,其中 0 <= i, j < n ,且这两个下标都可以被 k 整除,然后用从 j 开始的长度为 k 的子串替换从 i 开始的长度为 k 的子串。也就是说,将子串 word[i..i + k - 1] 替换为子串 word[j..j + k - 1] 。

返回使 word 成为 K 周期字符串 所需的 最少 操作次数。

如果存在某个长度为 k 的字符串 s,使得 word 可以表示为任意次数连接 s ,则称字符串 word 是 K 周期字符串 。例如,如果 word == "ababab",那么 word 就是 s = "ab" 时的 2 周期字符串 。

测试样例:

输入:word = "leetcodeleet", k = 4

输出:1

解释:可以选择 i = 4 和 j = 0 获得一个 4 周期字符串。这次操作后,word 变为 "leetleetleet" 。

解答:利用哈希表储存一下所有可能位置的出现次数,挑选出现次数最高的子字符串。

class Solution {
    public int minimumOperationsToMakeKPeriodic(String word, int k) {
        HashMap<String, Integer> count = new HashMap<>();
        int max = 0;
        for (int i = 0; i < word.length(); i = i + k) {
            String sub = word.substring(i, i + k);
            int tmp = count.getOrDefault(sub, 0) + 1;
            max = Math.max(max, tmp);
            count.put(sub, tmp);
        }
        return word.length() / k - max;
    }
}

100283. Minimum Length of Anagram Concatenation

给你一个字符串 s ,它由某个字符串 t 和它的 同位字符串 连接而成。

请你返回字符串 t 的 最小 可能长度。

同位字符串 指的是重新排列一个单词得到的另外一个字符串,原来字符串中的每个字符在新字符串中都恰好只使用一次。

测试样例:

输入:s = "abba"

输出:2

解释:一个可能的字符串 t 为 "ba" 。

解答:稍微有点脑经急转弯。查询所有出现的字母中的最大公约数,这样就能把所有字母按照要求的频次,平均分配到t中。注意英语的原文是a concatenation of anagrams of some string t。所以是先concatenation之后,再anagrams。中文翻译有点垃圾。

补充一下GCD不太够,可能会有aabb这种情况。所以增加一个前缀和数组,配合mod来判断是否真正可以完成计算。

class Solution {
    public int minAnagramLength(String s) {
        int[][] count = new int[s.length() + 1][26];
        for (int i = 0; i < s.length(); ++i) {
            for (int j = 0; j < 26; ++j) {
                count[i + 1][j] = count[i][j];
            }
            count[i + 1][s.charAt(i) - 'a'] += 1;
        }
        int max = -1;
        for (int n : count[s.length()]) {
            if (n > 0) {
                if (max == -1) {
                    max = n;
                } else {
                    max = gcd(max, n);
                }
            }
        }
        if (max == 1) {
            return s.length();
        }
        int realMax = s.length();
        for (int i = 1; i * i <= max; ++i) {
            if (max % i == 0) {
                int x1 = max / i, x2 = i;
                if (isValid(count, s.length() / x1, x1)) {
                    realMax = Math.min(realMax, s.length() / x1);
                }

                if (isValid(count, s.length() / x2, x2)) {
                    realMax = Math.min(realMax, s.length() / x2);
                }
            }
        }
        return realMax;
    }

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

    private boolean isValid(int[][] count, int x, int y) {
        for (int i = x; i < count.length; i = i + x) {
            for (int j = 0; j < 26; ++j) {
                if (count[i][j] - count[i - x][j] != count[count.length - 1][j] / y) {
                    return false;
                }
            }
        }
        return true;
    }
}

100288. Minimum Cost to Equalize Array

给你一个整数数组 nums 和两个整数 cost1 和 cost2 。你可以执行以下 任一 操作 任意 次:

  • 从 nums 中选择下标 i 并且将 nums[i] 增加 1 ,开销为 cost1。
  • 选择 nums 中两个 不同 下标 i 和 j ,并且将 nums[1] 和 nums[2] 都 增加 1 ,开销为 cost2 。

你的目标是使数组中所有元素都 相等 ,请你返回需要的 最小开销 之和。

由于答案可能会很大,请你将它对 10^9 + 7 取余 后返回。

测试样例:

输入:nums = [4,1], cost1 = 5, cost2 = 2

输出:15

解释:执行以下操作可以使数组中所有元素相等:

  • 将 nums[1] 增加 1 ,开销为 5 ,nums 变为 [4,2] 。
  • 将 nums[1] 增加 1 ,开销为 5 ,nums 变为 [4,3] 。
  • 将 nums[1] 增加 1 ,开销为 5 ,nums 变为 [4,4] 。

总开销为 15 。

解答:这题需要注意一个case。nums = [1,3,3], cost1 = 1000000, cost2 = 1。这种情况下,把所有数字调整成[5,5,5]是最合理的。没想到什么好的数学解法,我用的是暴力。2 * max - 1一定可以满足要求。暴力枚举所有可能,幸好1 <= nums[i] <= 10^6。这个范围也不大,计算一下耗时不长。

有更好的数学解法我再更新吧。

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

    public int minCostToEqualizeArray(int[] nums, int cost1, int cost2) {
        // 寻找最大值和最小值
        long max = 0, min = Integer.MAX_VALUE;
        for (int n : nums) {
            max = Math.max(max, n);
            min = Math.min(min, n);
        }

        // 如果cost1 * 2 <= cost2, 就没必要用任何优化,直接暴力将所有数字提升到max一致
        if (2 * cost1 <= cost2) {
            int res = 0;
            for (int n : nums) {
                long tmp = max - n;
                tmp *= cost1;
                res = (int) ((res + tmp) % mod);
            }
            return res;
        }

        long sum = 0;
        for (int n : nums) {
            sum += max - n;
        }
        long res = Long.MAX_VALUE;
        // 没想到什么好办法,就直接暴力枚举所有可能性了
        for (long i = 0; i <= Math.max(max, nums.length); ++i) {
            long cur = i * nums.length + sum;
            long maxDiff = max - min + i;
            long remain = cur - maxDiff;
            // 最大化的利用cost2的方案,但是最小值和最大值差距过大的话,还是有部分数字需要利用cost1提升
            if (maxDiff <= remain) {
                res = Math.min(res, (cur % 2 == 1 ? cost1 : 0) + cur / 2 * cost2);
            } else {
                res = Math.min(res, cost2 * remain + cost1 * (maxDiff - remain));
            }
        }
        return (int)(res % mod);
    }
}

Leave a Reply

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