前缀和及部分题目

前缀和

前缀和指数组的前 N项之和,是个比较基础的算法

力扣面试题 17.05. 字母与数字

给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。
返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。

示例 1:
输入: [“A”,“1”,“B”,“C”,“D”,“2”,“3”,“4”,“E”,“5”,“F”,“G”,“6”,“7”,“H”,“I”,“J”,“K”,“L”,“M”]
输出: [“A”,“1”,“B”,“C”,“D”,“2”,“3”,“4”,“E”,“5”,“F”,“G”,“6”,“7”]

示例 2:
输入: [“A”,“A”]
输出: []

提示:
array.length <= 100000

思路

使用前缀和+哈希
为字母则取1,数字为-1,即要求和为0的最大子数组。
前缀和表示第i个数及之前的所有数的和。哈希存储前缀和c第一次出现的位置i,循环时维护一个maxlen表示最大的子数组长度,start表示最长子数组起始下标。
初始化时,前缀和为0,下标为-1。遍历到i时,前缀和为sum,如果此时哈希表中没有关于前缀和为sum的记录,则更新indices[sum]=i。如果已有,则对比maxlen和i-indices[sum],如果i-indices[sum]较大,则maxlen更新为i-indices[sum],start更新为indices[sum]+1。
如果maxlen不为0,返回array中array[start]到array[start+maxlen]的部分。

代码

class Solution {
    
    
public:
    vector<string> findLongestSubarray(vector<string>& array) {
    
    
        int n=array.size();
        int sum=0;
        int maxlen=0;
        int start=-1;
        unordered_map<int,int> indices;
        indices[0]=-1;
        for(int i=0;i<n;i++){
    
    
            if(isalpha(array[i][0])){
    
    
                sum++;
            }
            else{
    
    
                sum--;
            }
            if(indices.find(sum)==indices.end()){
    
    
                indices[sum]=i;
            }
            else{
    
    
                if(i-indices[sum]>maxlen){
    
    
                    maxlen=i-indices[sum];
                    start=indices[sum]+1;
                }
            }
        }
        if(maxlen==0) return {
    
    };
        return vector<string>(array.begin()+start,array.begin()+start+maxlen);
    }
};

力扣209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:
输入:target = 4, nums = [1,4,4]
输出:1

示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105

进阶:
如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

思路

首先计算前缀和,presum[i]表示从0加到i-1的和,
对于遍历每个子数组的起始点nums[i],用二分搜索找到presum[j]-presum[i]大于或等于target的第一个下标j,遍历过程中维护最短子数组的长度minlen。

或者用滑动窗口做

代码

//前缀和+二分搜索
class Solution {
    
    
public:
    int minSubArrayLen(int target, vector<int>& nums) {
    
    
        int n=nums.size();
        int minlen=n+1;
        vector<int> presum(n+1,0);//presum[i]是从0加到i-1
        for(int i=0;i<n;i++){
    
    
            presum[i+1]=presum[i]+nums[i];
        }
        for(int i=0;i<n;i++){
    
    
            int l=i+1,r=n;
            while(l<r){
    
    
                int m=(l+r)/2;
                if(presum[m]-target<presum[i]){
    
    
                    l=m+1;
                }else{
    
    
                    r=m;
                }
            }
            if(presum[l]-target>=presum[i]){
    
    
                minlen=min(l-i,minlen);
            }
        }
        if(minlen>n) return 0;
        else return minlen;
    }
};
class Solution {
    
    
public:
    int minSubArrayLen(int target, vector<int>& nums) {
    
    
        int n=nums.size();
        int l=0,r=0;
        int sum=nums[0],minlen=n+1;
        while(r<n){
    
    
            while(sum<target&&r<n){
    
    
                
                if(++r<n) sum+=nums[r];
            }
            if(sum>=target){
    
    
                minlen=min(minlen,r-l+1);
                sum-=nums[l];
                l++;
            }
            else break;
        }
        return minlen<=n?minlen:0;
    }
};

牛客网拼多多2021笔试真题集

3.多多的求和计算
多多路上从左到右有N棵树(编号1~N),其中第i个颗树有和谐值Ai。
多多鸡认为,如果一段连续的树,它们的和谐值之和可以被M整除,那么这个区间整体看起来就是和谐的。
现在多多鸡想请你帮忙计算一下,满足和谐条件的区间的数量。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 256M,其他语言512M
输入描述:
第一行,有2个整数N和M,表示树的数量以及计算和谐值的参数。
( 1 <= N <= 100,000, 1 <= M <= 100 )
第二行,有N个整数Ai, 分别表示第i个颗树的和谐值。
( 0 <= Ai <= 1,000,000,000 )
输出描述:
共1行,每行1个整数,表示满足整体是和谐的区间的数量。
示例1
输入例子:
5 2
1 2 3 4 5
输出例子:
6
例子说明:
长度为1: [2], [4]
长度为2: 无
长度为3: [1,2,3], [3,4,5]
长度为4: [1,2,3,4], [2,3,4,5]
长度为5: 无
共6个区间的和谐值之和可以被2整除。

代码

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

int main() {
    
    
    int n,m;
    cin>>n>>m;
    long ans=0;
   
    vector<int> presum(n+1,0);
    unordered_map<int, vector<int>> mark;
    mark[0].push_back(0);
    for(int i=0;i<n;i++){
    
    
        int tree;
        cin>>tree;
        presum[i+1]=(presum[i]+tree%m)%m;
        if(mark.find(presum[i+1])!=mark.end()){
    
    
            int len=mark[presum[i+1]].size();
            ans+=len;
        }
        mark[presum[i+1]].push_back(i+1);
    }
    cout<<ans;
    return 0;
}
// 64 位输出请用 printf("%lld")

猜你喜欢

转载自blog.csdn.net/weixin_45184581/article/details/129462048