使用同余定理 解答 523. 连续的子数组和
作者:互联网
今天力扣的每日一题
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
子数组大小 至少为 2 ,且
子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。
如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。
光看题目很简单,只要两次遍历即可,下面是我写的第一个版本的代码。
1 public boolean checkSubarraySum(int[] nums, int k) { 2 for(int i = 0;i<nums.length-1;i++){ 3 int total = nums[i]; 4 for(int j = i+1;j<nums.length;j++){ 5 total += nums[j]; 6 if(total%k==0){ 7 return true; 8 } 9 } 10 } 11 return false; 12 }View Code
时间复杂度为O(n2),不出意外的超时了。
本题可以使用同余定理+前缀和来降低时间复杂度到O(n),请看以下两点。
1.同余定理
给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系。(百度百科的解释)
2.本题中,如果到i的前缀和对k取余 等于j的前缀和对k取余,则说明i到j之间的数之和为k的倍数。
根据以上分析,写出第二个版本的代码。
1 public boolean checkSubarraySum(int[] nums, int k) { 2 Map<Integer,Integer> map = new HashMap<>(); 3 int total= 0; 4 map.put(0,-1); 5 for(int i = 0;i<nums.length;i++){ 6 total = (total+nums[i])%k; 7 if(map.containsKey(total)){ 8 if(i-map.get(total)>1){ 9 return true; 10 } 11 }else{ 12 map.put(total,i); 13 } 14 } 15 return false; 16 }View Code
执行通过,25ms。
查看提交记录,居然有人的方案2ms就解决了,带着膜拜的心情欣赏一下。
1 public boolean checkSubarraySum(int[] nums, int k) { 2 for (int i = 0; i < nums.length - 1; i++) { 3 if (nums[i] == 0 && nums[i + 1] == 0) { 4 return true; 5 } 6 } 7 for (int i = 0; i < nums.length; i++) { 8 int sum = nums[i]; 9 for (int j = i + 1; j < nums.length; j++) { 10 sum += nums[j]; 11 if (sum % k == 0) { 12 return true; 13 } 14 } 15 if (sum < k) { 16 break; 17 } 18 } 19 return false; 20 }View Code
原来是在朴素算法的逻辑下,去掉了特殊情况,是我想复杂了。做算法题,还是要保持初心,尽量在自己能理解的情况下去优化,盲目使用一些看似高级的定理,一来不容易真正理解,二来也不一定能提升性能。
标签:return,nums,int,整数,数组,523,解答,同余 来源: https://www.cnblogs.com/jejas/p/14840980.html