要求:给你一个正整数数组 nums,请你移除 最短 子数组(可以为 空),使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。
请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。
子数组 定义为原数组中连续的一组元素。(题目来源:LeetCode)
思路一:暴力破解(O(n^3))
枚举每一种移除子数组的情况情况,用for循环嵌套。如数组长度为n,可以判断第一层循环要循环n-1轮,每轮可以循环取(n-当前轮数)次,每次取当前轮数个数。
(运行环境Java8)
public int minSubarray(int[] nums, int p) {
int a = nums.length;
long sum = 0;//用long防止取数超出int范围
for(int num:nums){
sum=sum+num;
}
if (sum % p == 0)
return 0; //整除时直接返回
if (a != 0) {
for (int i = 1; i < a; i++) { //取多少轮
for (int j = 0; j < a-i+1 ; j++) {//每轮取多少次
int k = 0;
for (int n = 0; n < i; n++) {//每次取多少个
k += nums[j+n];
}
if ((sum - k) % p == 0) {
return i;
}
}
}
return -1;
}
return a;
}
思路二:同余的方法(O(n))
结论:x%p==z,y%p==z则(x-y+p)%p==0。
在数学中一个正数x取余x%p==z(z为任意数),另一个正数y取余y%p==z,则(x-y)%p==0;如果是负数x与负数y当有x%p==z,y%p==z则(x-y)%p==0;而当x为正数y为负数时当有x%p==z,y%p==z则(x-y+p)%p==0,而当x为负数y为正数时当有x%p==z,y%p==z则(x-y+p)%p==0得对于任意下x,y满足x%p==z,y%p==z则(x-y+p)%p==0。
分析题目:由于题目要求从一个数组x中取一个数组y后可被p整除。即要(x-y)%p=0,只需要x%p=y%p=z。把题设数组sums[]可以得到前缀数组s[]例如:nums={1,2,3}则s={0,1,2,6}而nums中的{2,3}子数组的和为s[3]-s[1]。题设要求可写成s[n]-(s[right]-s[left])%p==0即s[right]-s[n]==s[left]%p。
public int minSubarray2(int [] nums,int p){
int n=nums.length;
int res=n;
long [] s=new long[n+1];
s[0]=0;
for(int i=0;i<n;++i){
s[i+1]=(s[i]+nums[i])%p;//s[left]%p
}
Map map= new HashMap<Integer,Integer>();
for (int i=0;i<n+1;i++){
map.put(s[i],i);
int j= (int) map.getOrDefault((s[i]-s[n]+p)%p,-n);
res=Math.min(res,i-j);
}
return res<n ? res:-1;
}