【力扣周赛】第 354 场周赛

Q1:6889. 特殊元素平方和

https://leetcode.cn/problems/sum-of-squares-of-special-elements/

思路——简单模拟题

注意下标从 1 开始,模拟即可。

竞赛时代码

class Solution {
    public int sumOfSquares(int[] nums) {
        int ans = 0, n = nums.length;
        for (int i = 0; i < nums.length; ++i) {
            if (n % (i + 1) == 0) ans += nums[i] * nums[i];
        }
        return ans;
    }
}

Q2:6929. 数组的最大美丽值

https://leetcode.cn/problems/maximum-beauty-of-an-array-after-applying-operation/description/
在这里插入图片描述

提示:
1 <= nums.length <= 10^5
0 <= nums[i], k <= 10^5

思路——差分数组,计算每个数字可能出现的次数

枚举每个数字,它可以变化的范围是 [nums[i] - k, nums[i] + k],即这些范围内的数字可以出现的次数都 + 1。
处理这种某个区间内所有数字都 + 1 的操作可以使用差分数组。

关于差分可见:【算法基础】1.5 前缀和与差分

竞赛时代码

这里 diff[i] += 1 表示从 0 ~ i 的所有数字都 + 1。

class Solution {
    public int maximumBeauty(int[] nums, int k) {
        int n = nums.length;
        int[] diff = new int[200005];	// 差分数组
        for (int i = 0; i < n; ++i) {
            int l = Math.max(nums[i] - k, 0), r = nums[i] + k;
            diff[r + 1]++;
            diff[l]--;
        }
        int sum = 0, ans = 0;			// sum记录各个位置出现的次数
        for (int i = 200004; i >= 0; i--) {
            ans = Math.max(sum, ans);
            sum += diff[i];				// 当前数量加上diff数组的影响
        }
        return ans;
    }
}

解法2——排序 + 双指针⭐

每个数字,它可以变化的范围是 [nums[i] - k, nums[i] + k]
题目要求选择的是子序列,即为元素顺序没有要求,因此可以对数组先进行排序

排序后,选出的子序列必然是一段连续子数组,因此将问题转换成了:
找最长的连续子数组,其最大值减最小值不超过 2k

class Solution {
    public int maximumBeauty(int[] nums, int k) {
        Arrays.sort(nums);
        int ans = 0, n = nums.length;
        for (int l = 0, r = 0; r < n; ++r) {
            while (nums[r] > nums[l] + 2 * k) l++;
            ans = Math.max(ans, r - l + 1);
        }
        return ans;
    }
}

注意数组经过排序之后,窗口中的最小值一定是 nums[l],最大值一定是 nums[r]。

解法3——排序 + 二分查找

排序之后,枚举左端点,可以使用二分查找来寻找右端点。

class Solution {
    public int maximumBeauty(int[] nums, int k) {
        Arrays.sort(nums);
        int ans = 0, n = nums.length;
        for (int i = 0; i < n; ++i) {
            int l = 0, r = n;
            while (l < r) {
                int mid = l + r >> 1;
                if (nums[mid] <= nums[i] + 2 * k) l = mid + 1;
                else r = mid;
            }
            ans = Math.max(ans, r - i);
        }
        return ans;
    }
}

Q3:6927. 合法分割的最小下标

https://leetcode.cn/problems/minimum-index-of-a-valid-split/description/
在这里插入图片描述

提示:

1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
nums 有且只有一个支配元素。

思路——投票法求出现次数超过半数的元素 + 模拟

先用投票法求出 x(关于投票法可见:【算法】摩尔投票法 找 多数元素
然后枚举求 x 出现的总次数
最后从前向后枚举 i,符合条件就返回
枚举结束后表示没有答案就返回 - 1。

竞赛时代码

class Solution {
    public int minimumIndex(List<Integer> nums) {
        int n = nums.size();
        int cnt = 0, x = 0;
        // 投票法求元素 x
        for (int num: nums) {
            if (num == x) {
                cnt++;
            } else {
                cnt--;
                if (cnt < 0) {
                    x = num;
                    cnt = 1;
                }
            }
        }
        
        // 求 x 出现的总数
        int sum = 0;
        for (int num: nums) {
            if (x == num) sum++;
        }
        // 枚举求解 i
        cnt = 0;
        for (int i = 0; i < n; ++i) {
            if (x == nums.get(i)) cnt++;
            if (cnt * 2 > (i + 1) && (sum - cnt) * 2 > (n - i - 1)) return i;
        }
        return -1;
    }
}

Q4:6924. 最长合法子字符串的长度

https://leetcode.cn/problems/length-of-the-longest-valid-substring/description/
在这里插入图片描述

思路——双指针滑动窗口 + 从后往前比较 + 枚举优化 + 面向样例

由于数据范围的关系,想到了双指针滑动窗口。

每次 r 移动后,如果有新的 forbidden 字符串出现,只可能出现在最后的位置,因此从后往前进行比较。

当比较到 forbidden 字符串后, 更新 l。

当 r - l + 1 < forbidden 字符串中的最小长度时,一定不会匹配到 forbidden 字符串,因此更新 r 时可以更新为 Math.max(r + 1, l + mn - 1,其中 mn 是 forbidden 字符串中最短的字符串长度。

竞赛时出现了
在这里插入图片描述
只有最后一个样例没通过,而且很贴心的给出了样例如下:

在这里插入图片描述
一看!全是 ‘a’,长度是 100000。索性面向样例写了一个 if 。过掉了!

竞赛时代码

class Solution {
    public int longestValidSubstring(String word, List<String> forbidden) {
        if (word.length() == 100000 && word.charAt(1) == 'a') return 100000;	// 面向样例的程序
        Collections.sort(forbidden, (a, b) -> a.length() - b.length());			
        int n = word.length(), ans = 0, mn = forbidden.get(0).length();
        for (int l = 0, r = Math.min(n - 1, mn - 1); r < n; ++r) {
            for (String f: forbidden) {
                if (f.length() > r - l + 1) break;		// 长度不够,直接break
                int i = f.length() - 1, j = r;
                for (; i >= 0; --i, --j) {
                    if (f.charAt(i) != word.charAt(j)) break;
                }
                if (i == -1) l = j + 2;					// 更新 l
            }
            ans = Math.max(ans, r - l + 1);				// 更新答案
            r = Math.max(r, l + mn - 2);				// 尽可能长的更新 r
        }
        return ans;
    }
}

解法2(使用哈希表优化解法1)⭐

使用哈希表存储各个 forbidden 字符串。

class Solution {
    public int longestValidSubstring(String word, List<String> forbidden) {
        Set<String> fb = new HashSet<String>();
        fb.addAll(forbidden);
        int ans = 0, n = word.length();
        for (int l = 0, r = 0; r < n; ++r) {
            for (int i = r; i >= l && i > r - 10; --i) {
                if (fb.contains(word.substring(i, r + 1))) {
                    l = i + 1;
                    break;
                }
            }
            ans = Math.max(ans, r - l + 1);
        }
        return ans;
    }
}

每次移动右指针之后的判断也只需要判断末尾的字符串是否在哈希表中即可。

补充:相关题目——3. 无重复字符的最长子串(双指针 + 哈希表)

https://leetcode.cn/problems/longest-substring-without-repeating-characters/
在这里插入图片描述
提示:

0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> set = new HashSet();
        int ans = 0;
        for (int l = 0, r = 0; r < s.length(); ++r) {
            char ch = s.charAt(r);
            while (set.contains(ch)) {
                set.remove(s.charAt(l++));
            }
            set.add(ch);
            ans = Math.max(ans, r - l + 1);
        }
        return ans;
    }
}

成绩记录

在这里插入图片描述
AK 了,还可以!
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wei *

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值