刷题15 滑动窗口

713. 乘积小于 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。

        滑动窗口 

int numSubarrayProductLessThanK(int* nums, int numsSize, int k) {
    int ans=0;
    int l=0;
    int sum=1;
    for(int r=0;r<numsSize;++r){
        sum*=nums[r];
        while(l<=r&&sum>=k){//需要防止k为0的情况,sum会一直大于k,l会越界
            sum/=nums[l];
            l++;
        }
        ans+=r-l+1;
    }
    return ans;
}

643. 子数组最大平均数 I 

给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。

请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

任何误差小于 10-5 的答案都将被视为正确答案。

输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
double findMaxAverage(int* nums, int numsSize, int k) {
    int ans=INT_MIN;
    int l=0;
    int sum=0;
    for(int r=0;r<numsSize;++r){
        sum+=nums[r];
        if(r-l+1>k){
            sum-=nums[l];
            l++;
        }
        //满足一个特定条件才会更新
        if(r-l+1==k){
            ans=fmax(ans,sum);
        }
    }
    return (double)ans/k;
}

 

3. 无重复字符的最长子串

 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

        滑动窗口+哈希表 

int lengthOfLongestSubstring(char* s) {
    int ans=0;
    int cnt[128]={0};
    int l=0,r=0,cur=0;
    int n=strlen(s);
    while(r<n){
        //第一次遇到的字母,r后移
        if(cnt[s[r]]==0){
            cnt[s[r]]++;
            cur++;
            r++;
        }else{
        //区间内有重复字母,此时r不会动,只有l动,直到消除了重复字符
            cnt[s[l]]--;
            cur--;
            l++;
        }
        ans=fmax(ans,cur);
    }
    return ans;
}

209. 长度最小的子数组

 给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
int minSubArrayLen(int target, int* nums, int numsSize) {
    int ans=INT_MAX;
    int l=0;
    int sum=0;
    for(int r=0;r<numsSize;++r){
        sum+=nums[r];
        while(sum>=target){
            ans=fmin(ans,r-l+1);//求最短长度,因此一旦满足条件,马上记录
            sum-=nums[l];
            l++;
        }
    }
    return ans==INT_MAX?0:ans;
}

 567. 字符串的排列

 给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。换句话说,s1 的排列之一是 s2 的 子串 。

输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").

        查找s2中是否有s1的排列,维护一个大小为n的滑动窗口 ,用哈希表记录窗口内的字母,直到发现与s1完全相同的字母排列。

bool check(int* cnt1,int* cnt2){
    for(int i=0;i<26;++i){
        if(cnt1[i]!=cnt2[i]){
            return false;
        }
    }
    return true;
}
bool checkInclusion(char* s1, char* s2) {
    int n=strlen(s1),m=strlen(s2);
    if(n>m) return false;
    int cnt1[26]={0},cnt2[26]={0};
    for(int i=0;i<n;++i){
        cnt1[s1[i]-'a']++;
        cnt2[s2[i]-'a']++;
    }
    if(check(cnt1,cnt2)) return true;
    for(int i=n;i<m;++i){
        //滑动窗口
        cnt2[s2[i]-'a']++;
        cnt2[s2[i-n]-'a']--;
        if(check(cnt1,cnt2)){
            return true;
        }
    }
    return false;
}

594. 最长和谐子序列

 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。

现在,给你一个整数数组 nums ,请你在所有可能的子序列中找到最长的和谐子序列的长度。

数组的子序列是一个由数组派生出来的序列,它可以通过删除一些元素或不删除元素、且不改变其余元素的顺序而得到。

输入:nums = [1,3,2,2,5,2,3,7]
输出:5
解释:最长的和谐子序列是 [3,2,2,2,3]

        虽然求的是子序列的长度,但是需要比较的只有一个序列中的最大值与最小值,且差正好是1,故可以先进行排序,在顺序序列中区找。 

int cmp(int* a,int* b){
    return *a-*b;
}
int findLHS(int* nums, int numsSize){
    qsort(nums,numsSize,sizeof(int),cmp);
    int ans=0;
    int l=0;
    for(int r=0;r<numsSize;++r){
        while(nums[r]-nums[l]>1){
            l++;
        }
        //满足条件才会更新ans,因为存在相减等于0的情况不应该更新
        if(nums[r]-nums[l]==1){
            ans=fmax(ans,r-l+1);
        }
    }
    return ans;
}

1512. 好数对的数目

给你一个整数数组 nums 。

如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。返回好数对的数目。

输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始

        对于每一个元素来说,若在它出现之前相同元素已经出现n次,则以该元素作为(i,j)中j的好数对个数为n,想要统计好数对的个数,就是要统计每一个元素前它已经出现的次数

int numIdenticalPairs(int* nums, int numsSize) {
    int hash[101]={0};
    int ans=0;
    for(int i=0;i<numsSize;++i){
        ans+=hash[nums[i]];
        hash[nums[i]]++;
    }
    return ans;
}

 2006. 差的绝对值为 K 的数对数目

给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i <=j 且 |nums[i] - nums[j]| == k 。|x| 的值定义为:

如果 x >= 0 ,那么值为 x 。

如果 x < 0 ,那么值为 -x 。

输入:nums = [1,2,2,1], k = 1
输出:4
解释:差的绝对值为 1 的数对为:
- [1,2,2,1]
- [1,2,2,1]
- [1,2,2,1]
- [1,2,2,1]

 要找满足i < j 且|nums[i] - nums[j]| == k的数对即对于j来说,找在j前面满足nums[i] = nums[j] - k 或nums[i] = nums[j] - k 的i的个数

int countKDifference(int* nums, int numsSize, int k) {
    int ans=0;
    int hash[101]={0};
    for(int i=0;i<numsSize;++i){
        int x1=nums[i]-k;
        if(x1>=0&&x1<101){
            ans+=hash[x1];
        }
        int x2=nums[i]+k;
        if(x2>=0&&x2<101){
            ans+=hash[x2];
        }
        hash[nums[i]]++;
    }
    return ans;
}



930. 和相同的二元子数组

 给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。子数组 是数组的一段连续部分。

输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:有 4 个满足题目要求的子数组:[1,0,1]、[1,0,1,0]、[0,1,0,1]、[1,0,1]

        首先将该数组进行加工,使得第i项为前i项的和,由于该题中要使子数组的和为goal,即要让nums[i]到nums[j]的和为goal,就是要让加工后的数组nums [ j ] - nums [ i-1 ] == goal,用i代换i-1,得nums [ j ] - nums [ i ] == goal,则nums [ j ] == nums [ i ] + goal

int numSubarraysWithSum(int* nums, int numsSize, int goal){
   //记录前缀和
   for(int i=1;i<numsSize;++i){
       nums[i]+=nums[i-1];//计算前缀和
   }
   int hash[30001]={0};
   int sum=0;
   //nums[i]-nums[j]==goal,则nums[j]==nums[i]-goal
   for(int i=0;i<numsSize;++i){
       int x=nums[i]-goal;
       if(x>=0){
           sum+=hash[x];
       }
       hash[nums[i]]++;
   }
   //最后加上前缀和为goal的个数
   sum+=hash[goal];
   return sum;
}

1004. 最大连续1的个数 III

 给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。

输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]粗体数字从 0 翻转到 1,最长的子数组长度为 6。
int longestOnes(int* nums, int numsSize, int k) {
    int ans=0;
    int l=0;
    int zero=0;
    for(int r=0;r<numsSize;++r){
        if(nums[r]==0){
            zero++;
        }
        while(zero>k){
            if(nums[l]==0) zero--;
            l++;
        }
        ans=fmax(ans,r-l+1);
    }
    return ans;
}

        前缀和+模拟   

        注意前缀和是从下标1开始存放的,presum[0]没有实际意义

        枚举两个子数组的起始坐标:第一个子数组的和为presum[i+firstLen]-presum[i]、第二个子数组的和为presum[j+secondLen]-presum[j];

        需要注意j需要分成两段进行遍历

int maxSumTwoNoOverlap(int* nums, int numsSize, int firstLen, int secondLen) {
    int presum[numsSize+1];
    int mmax=0;
    presum[0]=0;
    for(int i=1;i<=numsSize;++i){
        presum[i]=presum[i-1]+nums[i-1];
    }
    int i=0,j=0;//i标志firstlen长度的子数组起始位,j标志secondlen子数组的起始位
    for(i=0;i<=numsSize-firstLen;++i){
        //j在i前面
        for(j=0;j<=i-secondLen;++j){
            int cur=presum[i+firstLen]-presum[i]+presum[j+secondLen]-presum[j];
            if(mmax<cur){
                mmax=cur;
            }
        }
        //j在i后面
        for(j=i+firstLen;j<=numsSize-secondLen;++j){
            int cur=presum[i+firstLen]-presum[i]+presum[j+secondLen]-presum[j];
            if(mmax<cur){
                mmax=cur;
            }
        }
    }
    return mmax;
}

1156. 单字符重复子串的最大长度

 如果字符串中的所有字符都相同,那么这个字符串是单字符重复的字符串。

给你一个字符串 text,你只能交换其中两个字符一次或者什么都不做,然后得到一些单字符重复的子串。返回其中最长的子串的长度。

输入:text = "ababa"
输出:3
int maxRepOpt1(char* text) {
    int n=strlen(text);
    int l=0,r=0;
    int ans=0;
    int flag[26]={0};
    for(int i=0;i<n;++i) flag[text[i]-'a']++;
    while(r<n){
        while(r<n&&text[r]==text[l]){
            r++;
        }
        int nextleft=r;
        r++;//跳过一个不同的字母,继续查找
        while(r<n&&text[r]==text[l]){
            //只相隔一个字母,就继续累加
            r++;
        }
        int len=fmin(r-l,flag[text[l]-'a']);//最长不能超过同类字母的最大值
        ans=fmax(ans,len);
        r=nextleft;
        l=nextleft;
    }
    return ans;
}

1759. 统计同质子字符串的数目

给你一个字符串 s ,返回 s 中 同质子字符串 的数目。由于答案可能很大,只需返回对 109 + 7 取余 后的结果。

同质字符串 的定义为:如果一个字符串中的所有字符都相同,那么该字符串就是同质字符串。

子字符串 是字符串中的一个连续字符序列。

输入:s = "abbcccaa"
输出:13
解释:同质子字符串如下所列:
"a"   出现 3 次。
"aa"  出现 1 次。
"b"   出现 2 次。
"bb"  出现 1 次。
"c"   出现 3 次。
"cc"  出现 2 次。
"ccc" 出现 1 次。
3 + 1 + 2 + 1 + 3 + 2 + 1 = 13

        统计同质子序列的长度,每次加上即可 如a、aa、aaa,为3+2+1=6

int countHomogenous(char * s){
    int n=strlen(s);
    int ans=1;
    int tmp=1;
    //统计同质子序列的长度,每次加上即可
    for(int i=1;i<n;++i){
        if(s[i]==s[i-1]){
            tmp++;
        }else{
            tmp=1;
        }
        ans=(ans+tmp)%100000007;
    }
    return ans;
}

        标准滑动窗口解法: 

int countHomogenous(char * s){
    int n=strlen(s);
    int ans=0;
    int l=0,r=0;
    while(r<n){
        if(s[l]!=s[r]){
            //如果当前不是重复的字符
            l=r;
        }else{
            //如果当前是重复字符
            ans=(ans+r-l+1)%1000000007;
            r++;
        }
    }
    return ans;
}

1839. 所有元音按顺序排布的最长子字符串 

 当一个字符串满足如下条件时,我们称它是 美丽的 :

  • 所有 5 个英文元音字母('a' ,'e' ,'i' ,'o' ,'u')都必须 至少 出现一次。
  • 这些元音字母的顺序都必须按照 字典序 升序排布(也就是说所有的 'a' 都在 'e' 前面,所有的 'e' 都在 'i' 前面,以此类推)

比方说,字符串 "aeiou" 和 "aaaaaaeiiiioou" 都是 美丽的 ,但是 "uaeio" ,"aeoiu" 和 "aaaeeeooo" 不是美丽的 。

给你一个只包含英文元音字母的字符串 word ,请你返回 word 中 最长美丽子字符串的长度 。如果不存在这样的子字符串,请返回 0 。

输入:word = "aeiaaioaaaaeiiiiouuuooaauuaeiu"
输出:13
解释:最长子字符串是 "aaaaeiiiiouuu" ,长度为 13 。

        用type用于记录窗口中的字母的类别个数 

int longestBeautifulSubstring(char* word) {
    int n=strlen(word);
    if(n<5) return 0;
    int ans=0,type=1;
    int l=0;
    for(int r=1;r<n;++r){
        //字典序为逆序,重新计算窗口
        if(word[r-1]>word[r]){
            l=r;
            type=1;
        }
        //字典序顺序,但是遇到不同字母,要将种类加1
        if(word[r-1]<word[r]){
            type++;
        }
        //若遇到的是相同的字母,什么也不做,直接让r后移
        //如果字母的种类达到5,就更新答案
        if(type==5){
            ans=fmax(ans,r-l+1);
        }
    }
    return ans;
}

763. 划分字母区间 

 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。

返回一个表示每个字符串片段的长度的列表。

输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
int* partitionLabels(char* s, int* returnSize) {
    int cnt[26]={0};
    int n=strlen(s);
    //记录每个字符最远的下标
    for(int i=0;i<n;++i){
        cnt[s[i]-'a']=i;
    }
    int *ans=malloc(sizeof(int)*n);
    int l=0,r=0;
    int pos=0;
    for(int i=0;i<n;++i){
        //r为每个分组的最远的下标
        r=fmax(r,cnt[s[i]-'a']);
        if(i==r){
            ans[pos++]=r-l+1;
            l=r+1;
        }
    }
    *returnSize=pos;
    return ans;
}

 

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值