前后缀分解
除自身以外的的数组乘积
问题描述
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
约束:不能使用除法,且在 O(n)
时间复杂度内完成此题。
思路分析
首先不考虑约束,为了能快速算出nums
数组中除nums[i]
以外的所有元素的乘积,可以先计算出数组中所有元素的乘积,然后利用乘法的逆运算除法,在O(1)的时间复杂度内算出除自身以外的其它所有数的乘积。
代码
vector<int> productExceptSelf(vector<int>& nums) {
int n=nums.size(),product=1;
vector<int>ans(n);
for(int i:nums) product*=i;
for(int i:nums) ans.push_back(product/i);
return ans;
}
现在考虑不能用除法,也就是不能利用运算的逆运算来还原某个状态。对于这类问题可以使用前后缀分解的方法,首先从左往后顺序遍历,计算出前i
个数(从0计数)的乘积记为L[i]
,再从右往左逆序遍历,计算出后n-1-i
个数(从0计数)的乘积记为R[i]
,除nums[i]以外的所有数的乘积就是L[i-1]*R[i+1]
,L和R都是提前算好的,总时间复杂度为O(n)。
具体实现时,R现算现用,可以用一个变量代替。
代码
vector<int> productExceptSelf(vector<int>& nums) {
int n=nums.size();
vector<int>L(n),ans(n);
L[0]=1;
for(int i=0;i<n-1;i++){
L[i+1]=L[i]*nums[i];
}
int r=1;
for(int i=n-1;i>=0;i--){
ans[i]=L[i]*r;
r*=nums[i];
}
return ans;
}
最大或值
问题描述
给你一个下标从 0 开始长度为 n
的整数数组 nums
和一个整数 k
。每一次操作中,你可以选择一个数并将它乘 2
。
你最多可以进行 k
次操作,请你返回 nums[0] | nums[1] | ... | nums[n - 1]
的最大值。
a | b
表示两个整数 a
和 b
的 按位或 运算。
思路分析
题目k次操作后求所有元素的或值ans
最大值,一个正数要大,其二进制表示的位数越高越好。题目要求每次选一个数乘二(左移1位),一共操作k次。这k次操作全放在一个数上,最后或值ans才更大,明确这一点,我们只需求某个数左移k位后再与其它数求或值的最大值
即可。求所有的除自身外所有数或运算值
可以用前后缀分解在O(n)内求得(参见上题)。
代码
long long maximumOr(vector<int>& nums, int k) {
int n=nums.size();
vector<int>L(n);
for(int i=1;i<n;i++){
L[i]=L[i-1]|nums[i-1];
}
long long ans=0;
int r=0;
for(int i=n-1;i>=0;i--){
int s=L[i]|r;
r|=nums[i];
long long sum=nums[i]; //先用long long 类型接住,防止越界
sum<<=k;
sum|=s;
ans=max(sum,ans);
}
return ans;
}