“判断子数组能否被5整除的问题”题目要求
一、问题描述
小R有一个二进制数组 nums,其中的下标从 0 开始。我们定义 xi 为从最高有效位到最低有效位的子数组 nums[0..i] 所表示的二进制数。例如,如果 nums = [1, 0, 1],那么 x0 = 1,x1 = 2,x2 = 5。
小R想知道,对于每个 xi,它能否被 5 整除。你需要返回一个布尔值列表 answer,当 xi 能够被 5 整除时,answer[i] 为 true,否则为 false。
二、测试样例
样例1:
输入:
nums = [0, 1, 1]
输出:[True, False, False]
样例2:
输入:
nums = [1, 1, 1]
输出:[False, False, False]
样例3:
输入:
nums = [1, 0, 1, 1, 0]
输出:[False, False, True, False, False]
三、题目解析
3.1代码思路
- 初始化变量:需要一个布尔数组
answer来存储结果,并且需要一个变量来记录当前的二进制数模 5 的余数。 - 遍历数组:遍历输入数组
nums,对于每个元素,更新当前的二进制数模 5 的余数。 - 更新余数:每次遍历到一个新的元素时,将当前的二进制数左移一位(相当于乘以 2),然后加上当前元素的值,再对 5 取模。
- 记录结果:根据当前的余数是否为 0,决定
answer数组中对应位置的值。
3.2详细代码
import java.util.Arrays;
public class Main {
public static boolean[] solution(int[] nums) {
// 初始化结果数组
boolean[] answer = new boolean[nums.length];
// 初始化当前二进制数模 5 的余数
int currentMod = 0;
// 遍历数组
for (int i = 0; i < nums.length; i++) {
// 更新当前二进制数模 5 的余数
currentMod = (currentMod * 2 + nums[i]) % 5;
// 根据余数决定 answer 数组中的值
answer[i] = (currentMod == 0);
}
// 返回结果数组
return answer;
}
public static void main(String[] args) {
System.out.println(Arrays.equals(solution(new int[]{0, 1, 1}), new boolean[]{true, false, false}) ? 1 : 0);
System.out.println(Arrays.equals(solution(new int[]{1, 0, 1, 1, 0}), new boolean[]{false, false, true, false, false}) ? 1 : 0);
System.out.println(Arrays.equals(solution(new int[]{1, 1, 1}), new boolean[]{false, false, false}) ? 1 : 0);
}
}
四、知识总结
直接转换前缀数组为二进制数再判断是否能被 5 整除是不高效的,尤其是当数组长度较大时,前缀数可能会变得非常大。因此,我们采用模运算来优化计算:
-
模运算性质
模运算有一个重要性质:( a × b + c ) % m= [ ( a % m ) × b + c ] % m
利用这一性质,我们可以只关注二进制数对 5 的余数,从而避免直接计算前缀数的完整值。
-
状态更新
每次添加新的二进制位时,只需要更新当前的余数:currentMod = ( currentMod × 2 + nums[i] ) % 5
-
结果判断
如果当前余数为 0,则说明该前缀二进制数可以被 5 整除。
通过模运算的巧妙应用,这段代码高效解决了判断二进制前缀能否被 5 整除的问题。算法不仅逻辑清晰,而且避免了直接操作大整数可能导致的性能问题,非常适合处理大规模数据。在实际应用中,这种基于余数的动态更新方法也常用于诸如字符串匹配、哈希算法等场景。
五、复杂度分析
- 时间复杂度
遍历数组时,每个元素只需要常数时间更新currentMod和填充answer数组,时间复杂度为 O(n)。 - 空间复杂度
只需要额外的布尔数组answer,其空间复杂度为 O(n)。