前缀和刷题记录

        前缀和思想就是可以把连续子数组的元素和转换成两个前缀和的差值;

1. 区域和检索-数组不可变

303. 区域和检索 - 数组不可变

给定一个整数数组  nums,处理以下类型的多个查询:

  1. 计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的  ,其中 left <= right

实现 NumArray 类:

  • NumArray(int[] nums) 使用数组 nums 初始化对象
  • int sumRange(int i, int j) 返回数组 nums 中索引 left 和 right 之间的元素的 总和 ,包含 left 和 right 两点(也就是 nums[left] + nums[left + 1] + ... + nums[right] )
class NumArray {

    private int[] s; // 前缀和数组
    public NumArray(int[] nums) {
        s = new int[nums.length + 1]; // 要多一个s[0]元素
        for(int i=0; i< nums.length; i++) {
            s[i+1] = s[i] + nums[i]; // 计算前缀和
        }
    }
    
    // 计算元素和
    public int sumRange(int left, int right) {
        return s[right+1] - s[left]; // left -> right 的值就是s[right + 1] - s[left]
    }
}

2. 统计范围内的元音字符串数

2559. 统计范围内的元音字符串数

给你一个下标从 0 开始的字符串数组 words 以及一个二维整数数组 queries 。

每个查询 queries[i] = [li, ri] 会要求我们统计在 words 中下标在 li 到 ri 范围内(包含 这两个值)并且以元音开头和结尾的字符串的数目。

返回一个整数数组,其中数组的第 i 个元素对应第 i 个查询的答案。

注意:元音字母是 'a''e''i''o' 和 'u' 。

// class Solution {
//     // 题目要求:即给一个下标范围,需要words中的元素下标在这个范围内,且需要为元音开头和结尾
//     public int[] vowelStrings(String[] words, int[][] queries) {
//         // 如果 words[i] 符合要求,就视作 1,否则视作 0。
//         // 1.统计前缀和
//         int[] prefix = new int[words.length+1]; //要比words的长度大1
       
//         // 2. 计算前缀和
//         prefix[0] = 0; // prefix[n] 表示n之前有多少个元音字母
//         for(int i = 0; i < words.length; i++) {
//             // 判断是不是元音字母开头和结尾的
//             if(words[i].matches("[aeiou](.*[aeiou])?")) {
//                 prefix[i+1] =prefix[i] + 1; //比如word[0]为元音字符串,则prefix[1] = 1;
//             }else {
//                 prefix[i+1] = prefix[i];
//             }
//         }

//         // 3. 根据前缀和计算查询的答案
//         int[] ans = new int[queries.length]; //保存答案的数目
//         for(int i=0 ; i<queries.length; i++) {
//             // 每个query 是一个数组
//             ans[i] = prefix[queries[i][1]+1] - prefix[queries[i][0]];
//         }
//         return ans;
//     }
// }


// 下面的方式更快
class Solution {
    public int[] vowelStrings(String[] words, int[][] queries) {
        int n = words.length;
        int m = queries.length;
        int[] res = new int[m];

        //构造前缀和,简化到O(n)
        int[] prefix = new int[n+1];

        for (int i = 1; i <= n; i++) { // 计算前缀和
            prefix[i] = prefix[i - 1]; 
            if (isVowelString(words[i - 1])) {
                prefix[i] += 1; 
            }
        }

        // 得到最后的结果
        for (int i = 0; i < m; i++) {
            int[] curQuery = queries[i];
            res[i] = prefix[curQuery[1]+1] - prefix[curQuery[0]];
        }
        return res;
    }

    // 判断是不是元音字符串:即以元音字母开头与结尾
    public boolean isVowelString(String word) {
        return isVowelLetter(word.charAt(0)) && isVowelLetter(word.charAt(word.length() - 1));
    }

    public boolean isVowelLetter(char c) {
        return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
    }
}

3. 和有限的最长子序列

2389. 和有限的最长子序列

给你一个长度为 n 的整数数组 nums ,和一个长度为 m 的整数数组 queries 。

返回一个长度为 m 的数组 answer ,其中 answer[i] 是 nums 中 元素之和小于等于 queries[i] 的 子序列 的 最大 长度  。

子序列 是由一个数组删除某些元素(也可以不删除)但不改变剩余元素顺序得到的一个数组。

notice:

1. 对于子序列问题,如果元素的顺序没有影响,就可以先进行排序,一般排序之后可以更方便的操作,如可以在查找时使用二分法进行查找。

2. 前缀和数组prefix[i] 不一定表示的就是 a[0] ~ a[i-1] 元素之和;也可能表示的是a[0] ~ a[i] 元素之和;可以根据题目进行设置;如果题目中需要使用到两个前缀和相减,那为了能在某些情况减之后保留住a[0]元素,此时就可以把prefix[i]设置成a[0] ~ a[i-1];

class Solution {
    public int[] answerQueries(int[] nums, int[] queries) {
        // 特点:子序列
        // 常规思想:对于一些无序的数组是不是可以先进行排序
        // 排序之后再利用前缀和思想

        // 1. 排序
        Arrays.sort(nums); //改变原数组
        // 2. 如果每次选择最小的元素,那最后得到的子序列长度会最长
        // 2.1 求出前缀和
        int[] prefix = new int[nums.length];
        prefix[0] = nums[0];
        for(int i=1; i<nums.length; i++) { 
            prefix[i] = prefix[i-1] + nums[i] ; //这里的前缀和 prefix[i] 就表示前i个元素相加,如nums[1] = nums[0] + nums[1]
        }
        // 前缀和单调增大 [可使用二分查找来找到大于目标元素的第一个下标]
        int[] ans = new int[queries.length];
        for(int i=0; i<queries.length; i++) {
            ans[i] = binarySearch(prefix,queries[i]); 
        }
        return ans;
    }

    private int binarySearch(int[] prefix, int target) {
        int left = -1, right = prefix.length;
        while(left + 1 < right) {
            int mid = left + (right - left)/2;
            if(prefix[mid] > target) {
                right = mid;
            } else {
                left = mid;
            }
        }
        return right; // 如果没有找到,返回右边值
    }
}
// 前缀和可以直接记到 nums 中。
// 答案可以直接记到 queries 中。


3152. 特殊数组 II 1523
2438. 二的幂数组中查询范围内的乘积 1610
2055. 蜡烛之间的盘子 1819
1744. 你能在你最喜欢的那天吃到你最喜欢的糖果吗? 1859
53. 最大子数组和(题解:前缀和做法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值