连续的子数组和 [巧妙问题转换]

182 阅读1分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第7篇文章,连续的子数组和 [巧妙问题转换] - 掘金 (juejin.cn)

前言

算法题中有一类是专门应用某种定理而存在的题,是一种理解容易,想到很难,即问题转换的非常巧妙的一类题,通常称其为脑筋急转弯,全当长见识。

一、连续的子数组和

image.png

二、同余定理

package everyday.prefix;

import java.util.HashMap;
import java.util.Map;

// 连续的子数组和
public class CheckSubarraySum {
    /*
    做不来,看提示,当两个前缀和对k的余数相同时,且两者间距大于0,则存在这样的数。
    这是典型的脑筋急转弯型,因为问题转换的过于巧妙!
    朴素做法时间复杂度太高,从倍数角度  转到   余数角度 + 前缀和差值,复杂度降到线性级别。

    资料显示,这也称同余定理,当两数对同数k取余相等时,两数之差为k的倍数,理解容易,想到很难。
     */
    public boolean checkSubarraySum(int[] nums, int k) {
        // 记录余数的初始位置。
        Map<Integer, Integer> fx = new HashMap<>();
        // 初始化余数0,在-1位置上。
        fx.put(0, -1);
        // 计算前缀和,并同时记录余数的初始位置。
        int[] prefix = new int[nums.length];
        prefix[0] = nums[0];
        if (!fx.containsKey(prefix[0] % k)) fx.put(prefix[0] % k, 0);

        for (int i = 1; i < nums.length; i++) {
            prefix[i] = prefix[i - 1] + nums[i];

            int mod = prefix[i] % k;
            if (i - fx.getOrDefault(mod, i) > 1) return true;

            if (!fx.containsKey(mod)) fx.put(mod, i);
        }
        return false;
    }
}

总结

1)做不来的题马上看题解提示,然后coding,不浪费时间,也不浪费思考机会,做到真正的不浪费时间。

2)脑筋急转弯算法题,即问题转换的弯度过于大,刷它权当张见识。

3)同余定理,当两数对同数k取余相等时,两数之差为k的倍数。

参考文献

[1] LeetCode 连续的子数组和