元素都是正数,并且求数量一般都是可以使用滑动窗口
https://leetcode.cn/problems/minimum-size-subarray-sum/description/
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0;//滑动窗口的左边界
int res = nums.length+1,ans = 0;
for(int right = 0;right<nums.length;++right){
ans+=nums[right];
while(ans>=target){
res = Math.min(res,right-left+1);
ans -=nums[left++];
}
}
return res==nums.length+1?0:res;
}
}
https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/
可以使用数组 使用哈希表写起来也是比较复杂的
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> count = new HashMap<>();
int res = 0;
int left = 0;
for(int i = 0;i<s.length();++i){
Character c = s.charAt(i);
count.put(c,count.getOrDefault(c,0)+1);
while(count.get(c)>1){
count.put(s.charAt(left),count.get(s.charAt(left))-1);
++left;
}
res = Math.max(res,i-left+1);
}
return res;
}
}
https://leetcode.cn/problems/subarray-product-less-than-k/description/
这里我们right-left+1就是以right位置元素为结尾的符合条件的个数
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if(k==0||k==1)return 0;
int res = 0,ans = 1;
int left = 0;
for(int right = 0;right<nums.length;++right){
ans*=nums[right];
while(ans>=k){
ans/=nums[left];
++left;
}
res += right-left+1;
}
return res;
}
}
https://leetcode.cn/problems/length-of-longest-subarray-with-at-most-k-frequency/description/
class Solution {
public int maxSubarrayLength(int[] nums, int k) {
int res = 0; // 存储最终结果(最长子数组长度)
Map<Integer,Integer> map = new HashMap<>(); // 记录每个数字在当前窗口的出现次数
int left = 0, n = nums.length; // 滑动窗口的左边界和数组长度
for(int right = 0; right < n; ++right) {
int num = nums[right];
map.merge(num, 1, Integer::sum); // 更新当前数字的计数(增加1)
// 当当前数字的计数超过k时,移动左边界缩小窗口
while(map.get(num) > k) {
map.merge(nums[left], -1, Integer::sum); // 减少左边界数字的计数
left++; // 左边界右移
}
// 更新最大子数组长度(当前窗口大小:right - left + 1)
res = Math.max(res, right - left + 1);
}
return res;
}
}
https://leetcode.cn/problems/find-the-longest-semi-repetitive-substring/description/
class Solution {
public int longestSemiRepetitiveSubstring(String s) {
char[] sArr = s.toCharArray();
int l = 0,res = 1;
int same = 0;
//移动右指针,并统计相邻相同的情况出现了多少次,记作 same
for(int r = 1;r<sArr.length;++r){
if(sArr[r]==sArr[r-1])same++;
if(same>1){
//如果same>1,则不断移动左指针直到s[left]=s[left−1],此时将一对相同的字符移到窗口之外。然后将 same 置为 1
++l;
while(sArr[l]!=sArr[l-1])++l;
same = 1;
}
res = Math.max(res,r-l+1);
}
return res;
}
}
https://leetcode.cn/problems/max-consecutive-ones-iii/
class Solution {
public int longestOnes(int[] nums, int k) {
//统计0的个数 ct0 在ct0<=k 的情况下计算滑动窗口的最大值
int res = 0,l = 0,ct0 = 0;
for(int r=0;r<nums.length;++r){
ct0 += 1-nums[r];//使用1-nums[]代替判断 这样更方便 在后面的操作更方便
while(ct0>k){
ct0 -= 1-nums[l];
++l;
}
res = Math.max(res,r-l+1);
}
return res;
}
}
https://leetcode.cn/problems/count-subarrays-where-max-element-appears-at-least-k-times/description/
内层循环结束后,[left,right] 这个子数组是不满足题目要求的,但在退出循环之前的最后一轮循环,[left−1,right] 是满足题目要求的。由于子数组越长,越能满足题目要求,所以除了 [left−1,right],还有 [left−2,right],[left−3,right],…,[0,right] 都是满足要求的。也就是说,当右端点固定在 right 时,左端点在 0,1,2,…,left−1 的所有子数组都是满足要求的,这一共有 left 个,加到答案中
class Solution {
public long countSubarrays(int[] nums, int k) {
int max = 0;
for(int num:nums){
max= Math.max(max,num);
}
int l = 0,maxNums = 0;
long res = 0;
for(int r = 0;r<nums.length;++r){
if(nums[r]==max)++maxNums;
while(maxNums==k){
if(nums[l]==max)--maxNums;
++l;
}
//循环完之后[l,r]不符合要求 但是[l-1,r] ... [0,r]都是符合的
res+=l;
}
return res;
}
}
https://leetcode.cn/problems/count-subarrays-with-score-less-than-k/description/
越短越合法
class Solution {
public long countSubarrays(int[] nums, long k) {
int l = 0;
long sum = 0,res =0;
for(int r=0;r<nums.length;++r){
sum+=nums[r];
while(sum*(r-l+1)>=k){
sum-=nums[l];
++l;
}
res+=r-l+1;
}
return res;
}
}
https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/
逆向思维+滑动窗口
把问题转换成「从 nums 中移除一个最长的子数组,使得剩余元素的和为 x」。
class Solution {
public int minOperations(int[] nums, int x) {
int target = -x;
for(int num:nums){
target+=num;
}
//这里target+x == nums的所有数的和 我们从nums找到多少个可以等于target 那么剩下的就是等于x的
if(target<0)return -1;
int l=0,sum=0,res=-1;
for(int r=0;r<nums.length;++r){
sum+=nums[r];
while(sum>target){
sum-=nums[l];
++l;
}
if(sum==target){
res = Math.max(res,r-l+1);//尽可能使达到target的长度越长 那么达到x的长度就越短
}
}
return res<0?-1:nums.length-res;
}
}
https://leetcode.cn/problems/minimum-window-substring/
class Solution {
public String minWindow(String s, String t) {
int llen = s.length(),slen=t.length();
if(llen<slen)return "";
int[] cntS = new int[128]; // s 子串字母的出现次数
int[] cntT = new int[128]; // t 中字母的出现次数
for(char c:t.toCharArray()){
cntT[c]++;
}
int l=0,count=0,len = llen+1;
String res="";
for(int r=0;r<llen;++r){
cntS[s.charAt(r)]++; // 右端点字母移入子串
while(isCovered(cntS,cntT)){
if(len>(r-l+1)){
len = r-l+1;
res = s.substring(l,r+1);
}
cntS[s.charAt(l)]--; // 左端点字母移出子串
++l;
}
}
return res;
}
private boolean isCovered(int[] cntS, int[] cntT) {
for (int i = 'A'; i <= 'Z'; i++) {
if (cntS[i] < cntT[i]) {
return false;
}
}
for (int i = 'a'; i <= 'z'; i++) {
if (cntS[i] < cntT[i]) {
return false;
}
}
return true;
}
}
比较简单的思路,还有一个优化:每次都要花费 O(∣Σ∣) 的时间去判断是否涵盖,可以优化到 O(1) 可以看灵神的题解