构建前缀信息

构建前缀信息

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

  • 构建前缀和数组,快速计算子数组区间和
class NumArray {
    public int[] prefixSum;

    public NumArray(int[] nums) {
        prefixSum = new int[nums.length + 1];
        // 计算前缀和
        for (int i = 1; i <= nums.length; i++)
            prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
    }

    public int sumRange(int left, int right) {
        return prefixSum[right + 1] - prefixSum[left];
    }

}

未排序数组中累加和为给定值的最长子数组长度

  • 构建前缀和最早出现的位置,返回无序数组中累加和为给定值的最长子数组的长度
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int len = in.nextInt();
        int k = in.nextInt();
        int[] nums = new int[len];
        for (int i = 0; i < len; i++) {
            nums[i] = in.nextInt();
        }

        int res = 0;
        // 前缀和
        int[] prefixSum = new int[len + 1];
        // 记录前缀和第一次出现的位置
        Map<Integer, Integer> hashMap = new HashMap<>();
        // 初始就有一个前缀和为0
        hashMap.put(0, 0);
        for (int i = 1; i <= len; i++) {
            prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
            // 更新长度
            if (hashMap.containsKey(prefixSum[i] - k))
                res = Math.max(res, i - hashMap.get(prefixSum[i] - k));
            // 记录首次出现位置
            if (!hashMap.containsKey(prefixSum[i])) hashMap.put(prefixSum[i], i);
        }
        System.out.println(res);
    }
}
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int len = in.nextInt();
        int k = in.nextInt();
        int[] nums = new int[len];
        for (int i = 0; i < len; i++) {
            nums[i] = in.nextInt();
        }

        int res = 0;
        // 前缀和,只与前一项有关,可以省去数组
        int prefixSum = 0;
        // 记录前缀和第一次出现的位置
        Map<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(0, 0);
        for (int i = 1; i <= len; i++) {
            prefixSum += nums[i - 1];
            // 更新长度
            if (hashMap.containsKey(prefixSum - k))
                res = Math.max(res, i - hashMap.get(prefixSum - k));
            // 记录首次出现位置
            if (!hashMap.containsKey(prefixSum)) hashMap.put(prefixSum, i);
        }
        System.out.println(res);
    }
}

60. 和为 K 的子数组

  • 构建前缀和出现的次数,返回无序数组中累加和为给定值的子数组的个数
import java.util.HashMap;
import java.util.Map;

public class Solution {
    public int subarraySum(int[] nums, int k) {
        int res = 0;
        // 记录前缀和以及出现次数
        Map<Integer, Integer> map = new HashMap<>();
        // 前缀和为0的至少出现一次
        map.put(0, 1);
        int prefixSum = 0;
        for (int i = 1; i <= nums.length; i++) {
            prefixSum += nums[i - 1];
            res += map.getOrDefault(prefixSum - k, 0);
            // 累计出现次数
            map.put(prefixSum, map.getOrDefault(prefixSum, 0) + 1);
        }
        return res;
    }

    public static void main(String[] args) {
        int[] nums = {1, 1, 1};
        System.out.println(new Solution().subarraySum(nums, 2));
    }
}

未排序数组中累加和为给定值的最长子数组系列问题补1

  • 构建前缀和最早出现的位置,返回无序数组中,正负数个数相等的最长子数组的长度
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int len = in.nextInt();
        int[] nums = new int[len];
        for (int i = 0; i < len; i++)
            nums[i] = in.nextInt();

        int res = 0;
        // 记录前缀中正数和负数的差值(正数个数-负数个数)
        int prefix = 0;
        // 记录prefix和其位置
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 0);
        for (int i = 1; i <= len; i++) {
            if (nums[i - 1] > 0)
                prefix++;
            else if (nums[i - 1] < 0)
                prefix--;
            if (map.containsKey(prefix))
                // 找到该值上次出现的位置
                res = Math.max(res, i - map.get(prefix));
            else
                // 记录prefix首次出现位置
                map.put(prefix, i);
        }
        System.out.println(res);
    }
}
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int len = in.nextInt();
        int[] nums = new int[len];
        for (int i = 0; i < len; i++) {
            int temp = in.nextInt();
            // 接受输入数据的时候就处理
            nums[i] = temp != 0 ? (temp > 0 ? 1 : -1) : 0;
        }

        int res = 0;
        // 记录前缀中正数和负数的差值(正数个数-负数个数)
        int prefix = 0;
        // 记录prefix和其位置
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 0);
        for (int i = 1; i <= len; i++) {
            prefix += nums[i - 1];
            if (map.containsKey(prefix))
                // 找到该值上次出现的位置
                res = Math.max(res, i - map.get(prefix));
            else
                // 记录prefix首次出现位置
                map.put(prefix, i);
        }
        System.out.println(res);
    }
}

1124. 表现良好的最长时间段

  • 构建前缀和最早出现的位置。表现良好的最长时间段问题
import java.util.HashMap;
import java.util.Map;

class Solution {
    public int longestWPI(int[] hours) {
        int res = 0;
        // 记录前缀中大于8h的天数和小于等于8h的天数之差
        int prefix = 0;
        // 记录差值和下标
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 0);
        for (int i = 1; i <= hours.length; i++) {
            prefix += hours[i - 1] > 8 ? 1 : -1;
            if (prefix > 0) {
                // 从数组开头到当前位置符合要求
                res = i;
            } else {
                // 若当前prefix为-3,则找-4是否已经出现,若出现,则说明-4出现的位置到当前位置的子数组是符合条件的(大于8h的天数严格大于一半)
                // 若之前还有-5、-6啥的,其之前一定出现过-4,因为是从0开始加一减一的,先出现-4才可能出现-5、-6
                if (map.containsKey(prefix - 1))
                    res = Math.max(res, i - map.get(prefix - 1));
            }
            if (!map.containsKey(prefix)) map.put(prefix, i);
        }

        return res;
    }
}

1590. 使数组和能被 P 整除

  • 构建前缀和余数最早出现的位置。移除的最短子数组长度,使得剩余元素的累加和能被p整除
import java.util.HashMap;
import java.util.Map;

class Solution {
    public int minSubarray(int[] nums, int p) {
        // 总体和模p的余数,也是需要删除的部分的累加和模p的余数
        int delete = 0;
        for (int num : nums) delete = (delete + num) % p;

        // 不需要移除子数组
        if (delete == 0) return 0;

        int res = 0x7fffffff;
        // 记录累加和
        int prefixSum = 0;
        // key为模p的余数,value为该值最后一次出现的位置
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 0);
        for (int i = 1; i <= nums.length; i++) {
            prefixSum = (prefixSum + nums[i - 1]) % p;
            // 当前位置的前缀和模p的值为prefixSum,整体数组累加和模p的值为delete
            // 需要删除子数组的累加和模p的值为delete,这样删除元素后的整体数组模p的值才能为0,才能被p整除
            // 所以要往前寻找累加和模p的值为find最后一次出现的位置,才能使删除的数组长度最小
            int find = (prefixSum - delete + p) % p;
            if (map.containsKey(find))
                res = Math.min(res, i - map.get(find));
            // 记录prefixSum最后一次出现的位置
            map.put(prefixSum, i);
        }
        // 没得删或者要全删时返回-1
        return (res == 0x7fffffff || res == nums.length) ? -1 : res;
    }
}

1371. 每个元音包含偶数次的最长子字符串

  • 构建前缀奇偶状态最早出现的位置。每个元音包含偶数次的最长子串长度
import java.util.Arrays;

class Solution {
    public int findTheLongestSubstring(String s) {
        int len = s.length();
        // 只有5个元音字符,状态5位,状态总数32种
        int[] map = new int[32];
        // -2表示这个状态之前没出现过
        Arrays.fill(map, -2);
        // aeiou 00000
        map[0] = -1;
        int ans = 0;
        // status低5位从低到高分表表示aeiou的奇偶性,0为偶,1为奇
        int status = 0;
        // aeiou在status中对应的位置
        int m;
        for (int i = 0; i < len; i++) {
            // 当前字符
            m = move(s.charAt(i));
            // 情况1 : 当前字符不是元音,status不变
            // 情况2 : 当前字符是元音,a~u(0~4),修改相应的状态,亦或运算改变对应元音字符的奇偶性
            if (m != -1) status ^= 1 << m;
            if (map[status] != -2) {
                // 同样的状态,之前最早出现在哪
                ans = Math.max(ans, i - map[status]);
            } else {
                // 记录状态第一次出现的位置
                map[status] = i;
            }
        }
        return ans;
    }

    public static int move(char cha) {
        switch (cha) {
            case 'a':
                return 0;
            case 'e':
                return 1;
            case 'i':
                return 2;
            case 'o':
                return 3;
            case 'u':
                return 4;
            default:
                return -1;
        }
    }
}
posted @ 2024-02-08 00:47  n1ce2cv  阅读(10)  评论(0编辑  收藏  举报