LeetCode刷题笔记(7)
背景
今天是刷LeetCode的第七天,嗯,没有那么难受以及无法接受了,感觉还可以。今天结束了贪心算法的最后三道题目,明天就是二分法了。
763、划分字母区间
- 输入:S = “ababcbacadefegdehijhklij”
- 输出:[9,7,8]
- 解释:
- 划分结果为 “ababcbaca”, “defegde”, “hijhklij”。
- 每个字母最多出现在一个片段中。
- 像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少。
解题思路:
策略就是不断地选择从最左边起最小的区间。可以从第一个字母开始分析,
假设第一个字母是 ‘a’,那么第一个区间一定包含最后一次出现的 ‘a’。
但第一个出现的 ‘a’ 和最后一个出现的 ‘a’ 之间可能还有其他字母,
这些字母会让区间变大。举个例子,在 “abccaddbeffe” 字符串中,
第一个最小的区间是 “abccaddb”。
通过以上的分析,我们可以得出一个算法:对于遇到的每一个字母,
去找这个字母最后一次出现的位置,用来更新当前的最小区间。
算法:
定义数组 last[char] 来表示字符 char 最后一次出现的下标。
定义 anchor 和 j 来表示当前区间的首尾。
如果遇到的字符最后一次出现的位置下标大于 j,
就让 j=last[c] 来拓展当前的区间。
当遍历到了当前区间的末尾时(即 i==j ),
把当前区间加入答案,同时将 start 设为 i+1 去找下一个区间。
public class partitionLabels763 {
public List<Integer> partitionLabels(String S) {
int[] last = new int[26];
for(int i = 0; i <S.length(); ++i){
last[S.charAt(i)-'a']=i;
}
//for循环通过相同的字符作为下标来覆盖,
// 从而得到最后一个字符的位置,好奇妙啊
int j = 0,anchor = 0;
List<Integer> ans = new ArrayList<>();
for(int i = 0; i <S.length();++i){
j = Math.max(j,last[S.charAt(i)-'a']);
if(i == j){
ans.add(i-anchor+1);
anchor = i +1;
}
}
return ans;
}
}
总结:这道题目是中等难度的,官方给的答案非常巧妙。比如找到数组中某字母最后以此出现的位置,第一个for循环就利用覆盖来实现这种思想,我觉得非常好。稍微还是比较难想到的,利用j与last出现的下标进行比较。难就难在如何转化题目的含义。
665、判断非递减序列
- 给你一个长度为 n 的整数数组,
- 请你判断在 最多 改变 1 个元素的情况下,
- 该数组能否变成一个非递减数列。
- 我们是这样定义一个非递减数列的:
- 对于数组中所有的 i (0 <= i <= n-2),
- 总满足 nums[i] <= nums[i + 1]。
解题思路:
在出现 nums[i] < nums[i - 1] 时,
需要考虑的是应该修改数组的哪个数,
使得本次修改能使 i 之前的数组成为非递减数组,
并且 不影响后续的操作 。优先考虑令 nums[i - 1] = nums[i],
因为如果修改 nums[i] = nums[i - 1] 的话,
那么 nums[i] 这个数会变大,就有可能比 nums[i + 1] 大,
从而影响了后续操作。
还有一个比较特别的情况就是 nums[i] < nums[i - 2],
修改 nums[i - 1] = nums[i] 不能使数组成为非递减数组,
只能修改 nums[i] = nums[i - 1]。
public class checkPossibility665 {
public boolean checkPossibility(int[] nums){
int cnt = 0;
for(int i = 1; i <nums.length &&cnt<2;i++){
if(nums[i]>nums[i-1]){
continue;
}
cnt++;
if(i-2>=0 && nums[i-2]>nums[i]){
nums[i]=nums[i-1];
}else{
nums[i-1]=nums[i];
}
}
return cnt <=1;
}
}
总结:这道题目最难的地方在于想到i-2与i之间的大小关系,与我最初的想法不同,我本来的想法是计算出导致它不是递减的个数必须要小于nums.length-2
53、子数组最大的和
- 输入: [-2,1,-3,4,-1,2,1,-5,4],
- 输出: 6
- 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6
public class maxSubArray53 {
public int maxSubArray(int[] nums){
if(nums==null||nums.length==0) {
return 0;
}
int preSum = nums[0];
int maxSum = preSum;
for(int i = 1; i < nums.length; i++){
preSum = preSum>0 ? preSum+nums[i]:nums[i];
maxSum = Math.max(maxSum,preSum);
}
return maxSum;
}
}