文章目录
- 比赛链接
- Q1:6954. 统计和小于目标的下标对数目
- Q2:8014. 循环增长使字符串子序列等于另一个字符串
- 双指针
- 相似题目——392. 判断子序列
- Q3:6941. 将三个组排序
- 解法1——转化成最长非递减子序列
- 解法2——状态机DP
- Q4:8013. 范围中美丽整数的数目(数位DP)⭐⭐⭐⭐⭐
- 解法
- 使用到的技巧
- 判断这个数可以被 k 整除
- 判断奇数数位和偶数数位的数量之差
- 成绩记录
- 参考资料
比赛链接
https://leetcode.cn/contest/biweekly-contest-111
Q1:6954. 统计和小于目标的下标对数目
https://leetcode.cn/problems/count-pairs-whose-sum-is-less-than-target/
提示:
1 <= nums.length == n <= 50
-50 <= nums[i], target <= 50
解法1—— O ( n 2 ) O(n^2) O(n2)暴力
class Solution {public int countPairs(List<Integer> nums, int target) {int ans = 0, n = nums.size();for (int i = 0; i < n; ++i) {for (int j = i + 1; j < n; ++j) {if (nums.get(i) + nums.get(j) < target) ans++;}}return ans;}
}
解法2——排序+双指针 O ( n log n ) O(n\log{n}) O(nlogn)
排序之后使用双向双指针,
class Solution {public int countPairs(List<Integer> nums, int target) {Collections.sort(nums);int ans = 0, l = 0, r = nums.size() - 1;while (l < r) {if (nums.get(l) + nums.get(r) < target) {ans += r - l;l++;} else r--;}return ans;}
}
Q2:8014. 循环增长使字符串子序列等于另一个字符串
https://leetcode.cn/problems/make-string-a-subsequence-using-cyclic-increments/
提示:
1 <= str1.length <= 10^5
1 <= str2.length <= 10^5
str1 和 str2 只包含小写英文字母。
双指针
其实就是双指针判断子序列那道题目,除了可以相同匹配外,还可以循环+1后匹配。
class Solution {public boolean canMakeSubsequence(String s1, String s2) {int m = s1.length(), n = s2.length();int i = 0, j = 0;while (i < m && j < n) {// 如果可以匹配上if (s1.charAt(i) == s2.charAt(j) || (s1.charAt(i) + 1 - 'a') % 26 == (s2.charAt(j) - 'a') % 26) ++j;++i;}return j == n;}
}
相似题目——392. 判断子序列
https://leetcode.cn/problems/is-subsequence/description/
class Solution {public boolean isSubsequence(String s, String t) {int n1 = s.length(), n2 = t.length();int i = 0, j = 0;while (i < n1 && j < n2) {if (s.charAt(i) == t.charAt(j)) i++;j++;}return i == n1;}
}
Q3:6941. 将三个组排序
https://leetcode.cn/problems/sorting-three-groups/
提示:
1 <= nums.length <= 100
1 <= nums[i] <= 3
解法1——转化成最长非递减子序列
转换成最多可以保留多少个元素不变,这些保留的元素必须是非递减的。
那么答案就是除了这些保留的元素之外需要被删去的元素数量。
代码1—— O ( n 2 ) O(n^2) O(n2)dp
class Solution {public int minimumOperations(List<Integer> nums) {int n = nums.size(), ans = 0;int[] dp = new int[n];for (int i = 0; i < n; ++i) {dp[i] = 1;for (int j = 0; j < i; ++j) {if (nums.get(i) >= nums.get(j)) dp[i] = Math.max(dp[i], dp[j] + 1);}ans = Math.max(ans, dp[i]);}return n - ans;}
}
代码2——二分写法(更快 O ( n log n ) O(n\log{n}) O(nlogn))
class Solution {public int minimumOperations(List<Integer> nums) {int n = nums.size();List<Integer> g = new ArrayList<>();for (int i = 0; i < n; ++i) {int l = 0, r = g.size();while (l < r) {int mid = l + r >> 1;if (g.get(mid) <= nums.get(i)) l = mid + 1;else r = mid;}if (l == g.size()) g.add(nums.get(i));else g.set(l, nums.get(i));}return n - g.size();}
}
解法2——状态机DP
定义 f[i+1][j] 表示考虑 nums[0] 到 nums[i],且 nums[i] 变成 j 的最小修改次数。
class Solution {public int minimumOperations(List<Integer> nums) {int n = nums.size();int[][] dp = new int[n + 1][4];for (int i = 1; i <= n; ++i) {// 从0~i,且nums[i]变成jfor (int j = 1; j <= 3; ++j) {dp[i][j] = dp[i - 1][j];// 枚举第 i-1 个数字变成了 kfor (int k = 1; k < j; ++k) {dp[i][j] = Math.min(dp[i][j], dp[i - 1][k]);}if (j != nums.get(i - 1)) dp[i][j]++;}}int ans = n;for (int j = 1; j <= 3; ++j) {ans = Math.min(ans, dp[n][j]);}return ans;}
}
Q4:8013. 范围中美丽整数的数目(数位DP)⭐⭐⭐⭐⭐
https://leetcode.cn/problems/number-of-beautiful-integers-in-the-range/
提示:
0 < low <= high <= 10^9
0 < k <= 20
更多关于数位DP可见:
【算法】数位DP
【算法基础:动态规划】5.4 数位统计DP(计数问题)(数位DP)
解法
代码如下:
class Solution {int[][][] memo;char[] s;int m, k;public int numberOfBeautifulIntegers(int low, int high, int k) {this.k = k;return op(high) - op(low - 1);}public int op(int n) {s = Integer.toString(n).toCharArray();m = s.length;memo = new int[m][k][m * 2 + 1];for (int i = 0; i < m; ++i) {for (int j = 0; j < k; ++j) {Arrays.fill(memo[i][j], -1);}}return dfs(0, true, false, 0, m);}public int dfs(int i, boolean isLimit, boolean isNum, int val, int diff) {if (i == s.length) return isNum && val == 0 && diff == m? 1: 0;if (!isLimit && isNum && memo[i][val][diff] != -1) return memo[i][val][diff];int res = 0;if (!isNum) res = dfs(i + 1, false, false, 0, m);int up = isLimit? s[i] - '0': 9;for (int d = isNum? 0: 1; d <= up; ++d) {res += dfs(i + 1, isLimit && d == up, true, (val * 10 + d) % k, diff + d % 2 * 2 - 1);}if (!isLimit && isNum) memo[i][val][diff] = res;return res;}
}
使用到的技巧
判断这个数可以被 k 整除
由于是从前往后的,也就是从高位到低位,所以每次执行 (val * 10 + d) % k
判断奇数数位和偶数数位的数量之差
使用 diff
记录两者的数量之差,初始设置为 m,原因是作为数组的下标不能出现负数。
每次执行 diff + d % 2 * 2 - 1)
,这样如果 d 是奇数就会 + 1,否则就会 - 1。
成绩记录
就是很垃圾的成绩了。。。
参考资料
[第111场双周赛]式酱的解题报告