双周赛109
37分钟AK,T3初始值写错WA一次,满足
检查数组是否是好的【LC2784】
给你一个整数数组
nums
,如果它是数组base[n]
的一个排列,我们称它是个 好 数组。
base[n] = [1, 2, ..., n - 1, n, n]
(换句话说,它是一个长度为n + 1
且包含1
到n - 1
恰好各一次,包含n
两次的一个数组)。比方说,base[1] = [1, 1]
,base[3] = [1, 2, 3, 3]
。如果数组是一个好数组,请你返回
true
,否则返回false
。**注意:**数组的排列是这些数字按任意顺序排布后重新得到的数组。
-
思路:排序+模拟
排序后的好数组满足以下条件
- i ∈ [ 0 , n − 2 ] i \in [0,n-2] i∈[0,n−2]时,满足 n u m s [ i ] = = i + 1 nums[i]==i+1 nums[i]==i+1
- 最后一位满足 n u m s [ n − 1 ] = = n − 1 nums[n- 1]==n - 1 nums[n−1]==n−1
如果符合返回true,否则返回false
-
实现
javaclass Solution { public boolean isGood(int[] nums) { int n = nums.length; Arrays.sort(nums); for (int i = 0; i < n - 1; i++){ if (i + 1 != nums[i]){ return false; } } return nums[n - 1] == n - 1; } }
- 复杂度
- 时间复杂度: O ( n log n ) \mathcal{O}(n \log n) O(nlogn)
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 复杂度
将字符串中的元音字母排序【LC2785】
给你一个下标从 0 开始的字符串
s
,将s
中的元素重新 排列 得到新的字符串t
,它满足:
- 所有辅音字母都在原来的位置上。更正式的,如果满足
0 <= i < s.length
的下标i
处的s[i]
是个辅音字母,那么t[i] = s[i]
。- 元音字母都必须以他们的 ASCII 值按 非递减 顺序排列。更正式的,对于满足
0 <= i < j < s.length
的下标i
和j
,如果s[i]
和s[j]
都是元音字母,那么t[i]
的 ASCII 值不能大于t[j]
的 ASCII 值。请你返回结果字母串。
元音字母为
'a'
,'e'
,'i'
,'o'
和'u'
,它们可能是小写字母也可能是大写字母,辅音字母是除了这 5 个字母以外的所有字母。
-
思路:模拟+排序
先遍历一遍字符串,统计元音字符,将其按升序排序;再遍历一遍字符串,生成结果,辅音字母直接添加进sb中,元音字母按升序添加进sb中
-
实现
javaclass Solution { public String sortVowels(String s) { int n = s.length(); StringBuilder sb = new StringBuilder(); boolean[] isVowel = new boolean[n]; List<Character> vowels = new ArrayList<>(); for (int i = 0; i < n; i++){ char c = s.charAt(i); if (isVowel(c)){ isVowel[i] = true; vowels.add(c); } } Collections.sort(vowels, (c1, c2) -> c1 - c2); int j = 0; for (int i = 0; i < n; i++){ if (isVowel[i]){ sb.append(vowels.get(j)); j++; }else{ sb.append(s.charAt(i)); } } return sb.toString(); } public boolean isVowel(char c){ return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U'; } }
- 复杂度
- 时间复杂度: O ( n log n ) \mathcal{O}(n\log n) O(nlogn)
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 复杂度
访问数组中的位置使分数最大【LC2786】
给你一个下标从 0 开始的整数数组
nums
和一个正整数x
。你 一开始 在数组的位置
0
处,你可以按照下述规则访问数组中的其他位置:
- 如果你当前在位置
i
,那么你可以移动到满足i < j
的 任意 位置j
。- 对于你访问的位置
i
,你可以获得分数nums[i]
。- 如果你从位置
i
移动到位置j
且nums[i]
和nums[j]
的 奇偶性 不同,那么你将失去分数x
。请你返回你能得到的 最大 得分之和。
注意 ,你一开始的分数为
nums[0]
。
-
思路:dp
使用 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1]记录截止目前以奇数/偶数为末尾的最大分数。
-
状态转移
当前状态有前一个存在的状态转移得到,如果奇偶性不同,需要进行扣分
-
如果 n u m s [ i ] nums[i] nums[i]为奇数,当前状态可以由前一个奇数状态转移,或者由前一个偶数状态转移,此时还需要扣分,保留较大值
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + n u m s [ i ] − x ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]+nums[i]-x) dp[i][0]=max(dp[i−1][0],dp[i−1][1]+nums[i]−x) -
如果 n u m s [ i ] nums[i] nums[i]为偶数,当前状态可以由前一个奇数状态转移,或者由前一个偶数状态转移,此时还需要扣分,保留较大值
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + n u m s [ i ] − x ) dp[i][1]=max(dp[i-1][1],dp[i-1][0]+nums[i]-x) dp[i][1]=max(dp[i−1][1],dp[i−1][0]+nums[i]−x) -
1 < = n u m s [ i ] , x < = 1 0 6 1 <= nums[i], x <= 10^6 1<=nums[i],x<=106,设置无效状态为最小值,不会影响结果
-
-
初始状态取决于 n u m s [ 0 ] nums[0] nums[0]的奇偶性
- 如果 n u m s [ 0 ] nums[0] nums[0]为奇数, d p [ 0 ] [ 0 ] = n u m s [ 0 ] , d p [ 0 ] [ 1 ] = I n t e g e r . M I N _ V A L U E dp[0][0]=nums[0],dp[0][1]=Integer.MIN\_VALUE dp[0][0]=nums[0],dp[0][1]=Integer.MIN_VALUE
- 如果 n u m s [ 0 ] nums[0] nums[0]为偶数, d p [ 0 ] [ 1 ] = n u m s [ 0 ] , d p [ 0 ] [ 0 ] = I n t e g e r . M I N _ V A L U E dp[0][1]=nums[0],dp[0][0]=Integer.MIN\_VALUE dp[0][1]=nums[0],dp[0][0]=Integer.MIN_VALUE
-
-
实现
用变量表示
javaclass Solution { public long maxScore(int[] nums, int x) { long odd = Long.MIN_VALUE, even = Long.MIN_VALUE; int n = nums.length; long res = nums[0]; if (nums[0] % 2 == 0){ even = nums[0]; }else{ odd = nums[0]; } for (int i = 1; i < n; i++){ if (nums[i] % 2 == 0){ even = Math.max(even, even + nums[i]); // if (odd != Long.MIN_VALUE){ even = Math.max(odd + nums[i] - x, even); // } res = Math.max(res, even); }else{ odd = Math.max(odd, odd + nums[i]); // if (even != Long.MIN_VALUE){ odd = Math.max(even + nums[i] - x, odd); // } res = Math.max(res, odd); } } return res; } }
- 复杂度
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)
- 复杂度
将一个数字表示成幂的和的方案数【LC2787】
给你两个 正 整数
n
和x
。请你返回将
n
表示成一些 互不相同 正整数的x
次幂之和的方案数。换句话说,你需要返回互不相同整数[n1, n2, ..., nk]
的集合数目,满足n = n1x + n2x + ... + nkx
。由于答案可能非常大,请你将它对
109 + 7
取余后返回。比方说,
n = 160
且x = 3
,一个表示n
的方法是n = 23 + 33 + 53
。
-
思路:01背包
- 我们能够取的数字为「小于等于 n n n的正整数的
x
次幂」,需要满足的条件为「取的所有数之和为 n n n」,这些数有限并且只能取一次,因此可以转化为01背包问题- 物品重量为数值,价值为方案数1,求解背包容量为 n n n的总价值
- d p [ j ] dp[j] dp[j]:数目总和为 j j j的总方案数
- 我们能够取的数字为「小于等于 n n n的正整数的
-
实现
javaclass Solution { public static final int MOD = (int)(1e9 + 7); public int numberOfWays(int n, int x) { List<Integer> nums = new ArrayList<>();// 所有可能的x的幂 int res = 0; for (int i = 1; Math.pow(i, x) <= n; i++){ nums.add((int)Math.pow(i, x)); } // 01背包问题,不可重复 int[] dp = new int[n + 1]; dp[0] = 1; for (int i = 0; i < nums.size(); i++){ for (int j = n; j >= nums.get(i); j--){ dp[j] = (dp[j] + dp[j - nums.get(i)]) % MOD; } } return dp[n]; } }
- 复杂度
- 时间复杂度: O ( n ∗ m ) \mathcal{O}(n*m) O(n∗m),m为物品个数
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n)
- 复杂度