周赛354
半个小时过了前三题,第四题超时看了好久,没看到限制字符串的长度最大只有10
特殊元素平方和【LC2778】
给你一个下标从 1 开始、长度为
n
的整数数组nums
。对
nums
中的元素nums[i]
而言,如果n
能够被i
整除,即n % i == 0
,则认为num[i]
是一个 特殊元素 。返回
nums
中所有 特殊元素 的 平方和 。
-
思路
简单模拟
-
实现
class Solution { public int sumOfSquares(int[] nums) { int n = nums.length; int res = 0; for (int i = 0; i < n; i++){ if (n % (i + 1) == 0){ res += nums[i] * nums[i]; } } return res; } }
- 复杂度
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)
- 复杂度
数组的最大美丽值【LC2779】
给你一个下标从 0 开始的整数数组
nums
和一个 非负 整数k
。在一步操作中,你可以执行下述指令:
- 在范围
[0, nums.length - 1]
中选择一个 此前没有选过 的下标i
。- 将
nums[i]
替换为范围[nums[i] - k, nums[i] + k]
内的任一整数。数组的 美丽值 定义为数组中由相等元素组成的最长子序列的长度。
对数组
nums
执行上述操作任意次后,返回数组可能取得的 最大 美丽值。**注意:**你 只 能对每个下标执行 一次 此操作。
数组的 子序列 定义是:经由原数组删除一些元素(也可能不删除)得到的一个新数组,且在此过程中剩余元素的顺序不发生改变。
-
思路:差分+前缀和
每个
nums[i]
可以替换为范围[nums[i] - k, nums[i] + k]
内的任一整数,因此可以使用差分数组记录能被修改至每个区间的整数个数,最后将其累加,求出能修改至某个值的最大个数返回即可 -
实现
class Solution { public int maximumBeauty(int[] nums, int k) { // 每个数组能改变的范围[nums[i]-k, nums[i] + k]->差分统计->前缀和还原 求最大值 int mn = Integer.MAX_VALUE, mx = Integer.MIN_VALUE; int n = nums.length; for (int i = 0; i < n; i++){ mn = Math.min(mn, nums[i] - k); mx = Math.max(mx, nums[i] + k); } // mn负数-mn mx -= mn; int[] d = new int[mx + 2];//[0, mx] [mn, mx]->[0, mx-mn] for (int num : nums){ int l = num - k - mn, r = num + k - mn; d[l] += 1; d[r + 1] -= 1; } int res = 0, sum = 0; for (int i = 0; i <= mx; i++){ sum += d[i]; res = Math.max(res, sum); } return res; } }
- 复杂度
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 复杂度
合法分割的最小下标【LC2780】
如果元素
x
在长度为m
的整数数组arr
中满足freq(x) * 2 > m
,那么我们称x
是 支配元素 。其中freq(x)
是x
在数组arr
中出现的次数。注意,根据这个定义,数组arr
最多 只会有 一个 支配元素。给你一个下标从 0 开始长度为
n
的整数数组nums
,数据保证它含有一个支配元素。你需要在下标
i
处将nums
分割成两个数组nums[0, ..., i]
和nums[i + 1, ..., n - 1]
,如果一个分割满足以下条件,我们称它是 合法 的:
0 <= i < n - 1
nums[0, ..., i]
和nums[i + 1, ..., n - 1]
的支配元素相同。这里,
nums[i, ..., j]
表示nums
的一个子数组,它开始于下标i
,结束于下标j
,两个端点都包含在子数组内。特别地,如果j < i
,那么nums[i, ..., j]
表示一个空数组。请你返回一个 合法分割 的 最小 下标。如果合法分割不存在,返回
-1
。
-
思路
- 分割数组后,分割后的数组的支配元素仍和原数组相等。
- 先遍历数组,找到数组的支配元素及其出现次数
- 然后枚举分割位置,记录分割数组中支配元素的次数,如果分割后的数组合法,那么返回当前分割点
-
实现
class Solution { public int minimumIndex(List<Integer> nums) { // 分割数组使分割后数组的支配元素和原数组相等 Map<Integer,Integer> map = new HashMap<>(); int val = -1, mx = -1; int n = nums.size(); for (int i = 0; i < n; i++){ int num = nums.get(i); map.put(num, map.getOrDefault(num, 0) + 1); if (map.get(num) > mx){ val = num; mx = map.get(num); } } mx = map.get(val); int l = 0, r = mx; for (int i = 0; i < n - 1; i++){ if (nums.get(i) == val){// [0, i][i + 1, n - 1] l++; r--; } if (l * 2 > (i + 1) && r * 2 > (n - i - 1)){ return i; } } return -1; } }
- 复杂度
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 复杂度
最长合法子字符串的长度【LC2781】
给你一个字符串
word
和一个字符串数组forbidden
。如果一个字符串不包含
forbidden
中的任何字符串,我们称这个字符串是 合法 的。请你返回字符串
word
的一个 最长合法子字符串 的长度。子字符串 指的是一个字符串中一段连续的字符,它可以为空。
区间dp+字符串哈希[TLE]
我就是个努力优化的小废物hhh 刚开始写思路的时候,想到了用双指针找,最后直接用区间dp了
class Solution {
int base = 7;
public int longestValidSubstring(String word, List<String> forbidden) {
// 字符串哈希 + 区间dp
// dp[i][j]:字符串s[i, j]是否是合法字符串
// 使用字符串哈希求出forbidden中的值,并快速求出子字符串的哈希值,快速判断是否存在子字符串
// 使用双指针求出最长长度
int n = word.length();
long[] h = new long[n + 1];
long[] p = new long[n + 1];
p[0] = 1L;
for (int i = 1; i <= n; i++){
h[i] = h[i - 1] * base + word.charAt(i - 1);
p[i] = p[i - 1] * base;
}
Set<Long> ff = new HashSet<>();
for (String s : forbidden){
ff.add(hash(s));
}
boolean[][] dp = new boolean[n][n];
int res = 0;
for (int len = 1; len <= n; len++){
for (int l = 0; l + len - 1 < n; l++){
int r = l + len - 1;
dp[l][r] |= r - 1 >= 0 ? dp[l][r - 1] : false;
dp[l][r] |= l + 1 < n ? dp[l + 1][r] : false;
if (ff.contains(hash(l, r, h, p))){
dp[l][r] = true;// 包含
}
if (!dp[l][r]){
res = len;
}
if (res == n){
return res;
}
}
}
return res;
}
private long hash(int l , int r, long[] h, long[] p){
return h[r + 1] - h[l] * p[r - l + 1];
}
private long hash(String s){
int n = s.length();
long res = 0L;
for (int i = 0; i < n; i++){
res = res * base + s.charAt(i);
}
return res;
}
}
- 复杂度
- 时间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2)
- 空间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2)
哈希表+同向双指针
-
思路:哈希表+同向双指针
- 合法字符串:字符串中不含有forbidden中的任一字符串
- 双指针:固定左端点,枚举右端点,判断是否存在以右端点为结尾的子字符串出现在forbidden中,如果存在,那么右移左指针至不存在的位置。
- 判断过程:枚举以右端点为结尾的子字符串,长度从小到大枚举至10【forbidden中长度最大为10,长度为10以上的字符串肯定不在forbidden中,不需要判断】,如果 w o r d [ i , l e f t ] word[i,left] word[i,left]在forbidden中,那么合法字符串的左边界只能从 i + 1 i+1 i+1开始
-
实现
class Solution { public int longestValidSubstring(String word, List<String> forbidden) { var fb = new HashSet<String>(); fb.addAll(forbidden); int ans = 0, left = 0, n = word.length(); for (int right = 0; right < n; right++) { for (int i = right; i >= left && i > right - 10; i--) { if (fb.contains(word.substring(i, right + 1))) { left = i + 1; // 当子串右端点 >= right 时,合法子串一定不能包含 word[i] break; } } ans = Math.max(ans, right - left + 1); } return ans; } } 作者:灵茶山艾府 链接:https://leetcode.cn/problems/length-of-the-longest-valid-substring/solutions/2345796/ha-xi-biao-shuang-zhi-zhen-pythonjavacgo-bcez/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 复杂度
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度: O ( m ) \mathcal{O}(m) O(m),forbidden的大小
- 复杂度