100248. Existence of a Substring in a String and Its Reverse

给你一个字符串 s ,请你判断字符串 s 是否存在一个长度为 2 的子字符串,在其反转后的字符串中也出现。

如果存在这样的子字符串,返回 true;如果不存在,返回 false 。

测试样例:

输入:s = "leetcode"

输出:true

解释:子字符串 "ee" 的长度为 2,它也出现在 reverse(s) == "edocteel" 中。

解答:无脑暴力。

class Solution {
    public boolean isSubstringPresent(String s) {
        String reverse = new StringBuilder(s).reverse().toString();
        for (int i = 0; i < s.length(); ++i) {
            StringBuilder tmp = new StringBuilder();
            for (int j = i; j < s.length(); ++j) {
                tmp.append(s.charAt(j));
                if (j > i && reverse.contains(tmp)) {
                    return true;
                }
            }
        }
        return false;
    }
}

100236. Count Substrings Starting and Ending with Given Character

给你一个字符串 s 和一个字符 c 。返回在字符串 s 中并且以 c 字符开头和结尾的非空子字符串的总数。

测试样例:

输入:s = "abada", c = "a"

输出:6

解释:以 "a" 开头和结尾的子字符串有: "abada"、"abada"、"abada"、"abada"、"abada"、"abada"。

解答:排列组合题。

class Solution {
    public long countSubstrings(String s, char c) {
        int count = 0;
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) == c) {
                ++count;
            }
        }
        if (count == 0) {
            return 0;
        }
        return (1L + count) * count / 2;
    }
}

100255. Minimum Deletions to Make String K-Special

给你一个字符串 word 和一个整数 k。

如果 |freq(word[i]) - freq(word[j])| <= k 对于字符串中所有下标 i 和 j 都成立,则认为 word 是 k 特殊字符串。

此处,freq(x) 表示字符 x 在 word 中的出现频率,而 |y| 表示 y 的绝对值。

返回使 word 成为 k 特殊字符串 需要删除的字符的最小数量。

测试样例:

输入:word = "aabcaba", k = 0

输出:3

解释:可以删除 2 个 "a" 和 1 个 "c" 使 word 成为 0 特殊字符串。word 变为 "baba",此时 freq('a') == freq('b') == 2。

解答:这道题目需要注意,除了把出现频次较大的数减小来满足要求,也能直接把出现频次较小的数字完全干掉来完成要求。小写字母一共就26个,排序之后,直接暴力枚举两种情况,就能获取结果。

class Solution {
    public int minimumDeletions(String word, int k) {
        int[] count = new int[26];
        for (int i = 0; i < word.length(); ++i) {
            count[word.charAt(i) - 'a'] += 1;
        }
        Arrays.sort(count);
        int res = word.length();
        int prefixSum = 0;
        for (int i = 0; i < 26; ++i) {
            int result = prefixSum;
            for (int j = i + 1; j < 26; ++j) {
                if (count[j] > count[i] + k) {
                    result += count[j] - count[i] - k;
                }
            }
            res = Math.min(res, result);
            prefixSum += count[i];
        }
        return res;
    }
}

100227. Minimum Moves to Pick K Ones

给你一个下标从 0 开始的二进制数组 nums,其长度为 n ;另给你一个 正整数 k 以及一个 非负整数 maxChanges 。

灵茶山艾府在玩一个游戏,游戏的目标是让灵茶山艾府使用 最少 数量的 行动 次数从 nums 中拾起 k 个 1 。游戏开始时,灵茶山艾府可以选择数组 [0, n - 1] 范围内的任何索引index 站立。如果 nums[index] == 1 ,灵茶山艾府就会拾起一个 1 ,并且 nums[index] 变成0(这 不算 作一次行动)。之后,灵茶山艾府可以执行 任意数量 的 行动(包括零次),在每次行动中灵茶山艾府必须 恰好 执行以下动作之一:

  • 选择任意一个下标 j != index 且满足 nums[j] == 0 ,然后将 nums[j] 设置为 1 。这个动作最多可以执行 maxChanges 次。
  • 选择任意两个相邻的下标 x 和 y(|x - y| == 1)且满足 nums[x] == 1, nums[y] == 0 ,然后交换它们的值(将 nums[y] = 1 和 nums[x] = 0)。如果 y == index,在这次行动后灵茶山艾府拾起一个 1 ,并且 nums[y] 变成 0 。

返回灵茶山艾府拾起 恰好 k 个 1 所需的 最少 行动次数。

测试样例:

输入:nums = [1,1,0,0,0,1,1,0,0,1], k = 3, maxChanges = 1

输出:3

解释:如果游戏开始时灵茶山艾府在 index == 1 的位置上,按照以下步骤执行每个动作,他可以利用 3 次行动拾取 3 个 1 :

  • 游戏开始时灵茶山艾府拾取了一个 1 ,nums[1] 变成了 0。此时 nums 变为 [1,0,0,0,0,1,1,0,0,1] 。
  • 选择 j == 2 并执行第一种类型的动作。nums 变为 [1,0,1,0,0,1,1,0,0,1]
  • 选择 x == 2 和 y == 1 ,并执行第二种类型的动作。nums 变为 [1,1,0,0,0,1,1,0,0,1] 。由于 y == index,灵茶山艾府拾取了一个 1 ,nums 变为 [1,0,0,0,0,1,1,0,0,1] 。
  • 选择 x == 0 和 y == 1 ,并执行第二种类型的动作。nums 变为 [0,1,0,0,0,1,1,0,0,1] 。由于 y == index,灵茶山艾府拾取了一个 1 ,nums 变为 [0,0,0,0,0,1,1,0,0,1] 。

请注意,灵茶山艾府也可能执行其他的 3 次行动序列达成拾取 3 个 1 。

解答:这道题目难度有点大。有些特殊情况,我不知道怎么处理,代码里面就类似hard code了。特殊的情况指1,1,1这种,因为只要用操作2就能完成一次1获取,比直接线增加一个1,然后操作2获取更快。这种情况我就特殊处理了。

其他情况就比较简单的,我们只要寻找一个区间能够满足sum(i...j) + maxChanges >= k,然后取当中那个1出现的位置,计算操作数就行了。涉及到绝对值累和,可以利用前缀和来完成。

class Solution {
    public long minimumMoves(int[] nums, int k, int maxChanges) {
        int n = nums.length, maxC = maxContinue(nums);
        if (maxC == 0) {
            return 2L * k;
        } else if (maxC >= k) {
            return k - 1;
        } else if (maxChanges + maxC >= k) {
            return maxC - 1 + (k - maxC) * 2L;
        } else {
            List<Long> prefixSum = new ArrayList<>();
            List<Long> posMem = new ArrayList<>();
            prefixSum.add(0L);
            for (int i = 0; i < nums.length; ++i) {
                if (nums[i] == 1) {
                    posMem.add((long) i);
                    prefixSum.add(prefixSum.get(prefixSum.size() - 1) + i);
                }
            }
            int st = 0, ed = k - maxChanges - 1;
            long res = Long.MAX_VALUE;
            while (ed < posMem.size()) {
                int mid = (st + ed) / 2;
                long sumLeft = prefixSum.get(mid + 1) - prefixSum.get(st);
                long sumRight = prefixSum.get(ed + 1) - prefixSum.get(mid + 1);
                long result = (mid - st + 1L) * posMem.get(mid) - sumLeft + sumRight - (ed - mid) * posMem.get(mid);
                result += 2L * maxChanges;
                res = Math.min(res, result);
                ++st;
                ++ed;
            }
            return res;
        }
    }

    private int maxContinue(int[] nums) {
        int res = 0;
        for (int i = 0; i < nums.length; ++i) {
            int count = 0;
            if (nums[i] == 1) {
                ++count;
                if (i - 1 >= 0 && nums[i - 1] == 1) {
                    ++count;
                }
                if (i + 1 < nums.length && nums[i + 1] == 1) {
                    ++count;
                }
            }
            res = Math.max(res, count);
        }
        return res;
    }
}

Leave a Reply

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