求
最小值
先贴一个思路很棒的优化,充分利用单调不增的性质
记AND(l,r)
表示
,根据&运算规则我们知道 a < a&b,因此可推得
所以当AND(l,r)<=t
时就可以提前退出了
此外,考虑另一种情况:AND递减的最小值仍大于t。这说明从某一位k
开始,后面的数在某些位上都是1,且AND(k,len-1)>=t
,那么k
之后,无论怎么组合,结果都不会比AND(k,len-1)
更小,所以也可以提前退出,确定答案。
以上是Leetcode讨论区中给出的方法,但是这个方法仍不够完美,因为其对于某些特殊情况仍会恶化至O(n2)。我们来考虑如下数据:
target = 2
arr = {5,5,5,5,5......1}
arr
长为1e5,除了最后最后一位是1,其他都是5。
按之前给出的流程,存在sum<=t
的情况,但是发生在数组末尾,所以这时程序性能急剧恶化,退化到了O(n2)的情况。为了应对这种情况,可以考虑在遍历时跳过相等且连续的元素,这样不仅不影响正确性且避免了重复的计算,详见代码
class Solution {
public int closestToTarget(int[] arr, int t) {
int ans = Math.abs(arr[0] - t);
int len = arr.length;
for(int l=0;l<len;l++){
int sum = arr[l];
for(int r=l;r<len;r++){
sum = sum & arr[r];
ans = Math.min(ans,Math.abs(sum - t));
if(sum <= t) break;
}
if(sum >= t) break;
int pos = l+1;
while(pos<len && arr[pos]==arr[l])
pos++;
l = pos - 1;
}
return ans;
}
}
其实这样好像也不对,因为这个递减的性质有个默认的设定,就是
任意子区间AND运算后结果都要大于t。若
取绝对值后,若仍保持递减性质则需
但是显然,这样的性质是难以保证的
附讨论区另一题解
ネコではないです
仍然根据&运算的性质,对于arr[i]
来说,后续的状态最多之后32种。因为&的过程就是将1flip为0的过程,所以最多每次翻一位,也就是32次。所以借助set集和不重复的特性记录不同的状态,然后移动到新的位置更新和添加状态就能遍历所有子区间的结果
class Solution {
public int closestToTarget(int[] arr, int t) {
int ans = Math.abs(arr[0] - t);
int len = arr.length;
HashSet<Integer> now = new HashSet();
for(int l=0;l<len;l++){
HashSet<Integer> tmp = new HashSet();
for(Integer ele : now){
tmp.add(ele & arr[l]);
}
tmp.add(arr[l]);
for(Integer ele : tmp){
ans = Math.min(ans,Math.abs(ele - t));
}
now = tmp;
}
return ans;
}
}