- 线段树解法 + 二分
class Solution {
public int minimumDifference(int[] nums, int k) {
this.nums = nums;
this.n = nums.length;
return check(k);
}
public static void main(String[] args) {
Solution solution = new Solution();
int ans = solution.minimumDifference(new int[]{
9,71,98,44,30
}, 26);
System.out.println(ans);
}
public int check(int k) {
Seg seg = build(0, n - 1);
int ans = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
int l = i;
int r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
int val = seg.query(i, mid);
ans = Math.min(ans, Math.abs(val - k));
if (val < k) {
s // 往大的方向靠
// 也就是右端点要向靠近l
r = mid - 1;
} else if (val == k) {
return 0;
} else {
// > k, 往小的方向靠
// 也就是右端点要靠近r
l = mid + 1;
}
}
}
return ans;
}
class Seg {
int l, r;
Seg left, right;
int value;
public Seg(int l, int r) {
this.l = l;
this.r = r;
left = null;
right = null;
}
public int query(int l, int r) {
if (this.l > r || this.r < l) {
return Integer.MAX_VALUE;
}
if (l <= this.l && this.r <= r) {
return this.value;
}
return this.left.query(l, r) & this.right.query(l, r);
}
}
int[] nums;
int n;
public Seg build(int l, int r) {
if (l == r) {
Seg seg = new Seg(l, l);
seg.value = nums[l];
return seg;
}
Seg left = build(l, (l + r) / 2);
Seg right = build((l + r) / 2 + 1, r);
Seg seg = new Seg(l, r);
seg.left = left;
seg.right = right;
seg.value = left.value & right.value;
return seg;
}
}
- 双指针解法
class Solution {
public boolean hasMask(int num, int i) {
return (num & (1 << i)) != 0;
}
public static void main(String[] args) {
Solution solution = new Solution();
int ans = solution.minimumDifference(new int[]{
1, 10, 6
}, 7);
System.out.println(ans);
}
public int minimumDifference(int[] nums, int k) {
// dp[i][b] 表示前i个数第b位为0个数
int n = nums.length;
int[][] dp = new int[n][32];
for (int i = 0; i < n; i++) {
for (int b = 31; b >= 0; b--) {
if (i == 0) {
dp[i][b] = hasMask(nums[i], b) ? 0 : 1;
continue;
}
dp[i][b] = dp[i - 1][b] + (hasMask(nums[i], b) ? 0 : 1);
}
}
//
int j = -1;
int ans = Integer.MAX_VALUE;
int x = Integer.MAX_VALUE;
while (j < n && x > k) {
j++;
if (j == n) {
break;
}
x = x & nums[j];
ans = Math.min(ans, Math.abs(x - k));
}
// [0..j] 其中a0...aj是小于等于k的。
// [i..j]开始重新计算
for (int i = 1; i < nums.length && j < n; i++) {
if (j < i) {
j = i;
x = nums[i];
} else {
int last = nums[i - 1];
// 考虑哪些为0的位,是否会改变,为1的位是没有变化的
// [i-1,j][b]有多少个0
for (int b = 31; b >= 0; b--) {
if (!hasMask(x, b) && !hasMask(last, b) && isGreater0(i, j, b, dp)) {
// need change.
x = x + (1 << b);
}
}
}
ans = Math.min(ans, Math.abs(x - k));
while (j<n && x > k) {
j++;
if ( j == n) {
break;
}
x = x & nums[j];
ans = Math.min(ans, Math.abs(x - k));
}
}
return ans;
}
public boolean isGreater0(int i, int j, int b, int[][] dp) {
int x = dp[i - 1][b];
int y = dp[j][b];
int zero = y - x;
int one = j - i + 1 - zero;
return zero == 0 && one >=1;
}
}
- st 表
class Solution {
static int[] log = new int[100001];// 2^log[i] 表示最接近 i 的 2 的某次方
static {
log[0] = -1;
for (int i = 1; i <= 100000; i++) {
log[i] = log[i >> 1] + 1;
}
}
int[][] st;
public int minimumDifference(int[] nums, int k) {
int n = nums.length;
st = new int[n][18];
// st 表预处理
for (int i = 0; i < n; i++) {
st[i][0] = nums[i];
}
for (int j = 1; j < 18; j++) {
for (int i = 0; i + (1 << j) - 1 < n; i++) {
st[i][j] = st[i][j - 1] & st[i + (1 << (j - 1))][j - 1];
}
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
int l = left(0, i, k), r = right(0, i, k);
if (l != -1) {
ans = Math.min(ans, k - l);
}
if (r != -1) {
ans = Math.min(ans, r - k);
}
}
return ans;
}
// 固定 r 时, nums[l..r] 按位与值 <= k 的最大值,如果找不到返回 -1
public int left(int l, int r, int k) {
int b = r, m, ans = -1;
while (l <= r) {
m = (l + r) >> 1;
int v = query(m, b);// 获取 nums[m..b] 的区间与运算值
if (v <= k) {
ans = v;
l = m + 1;
} else {
r = m - 1;
}
}
return ans;
}
// 固定 r 时, nums[l..r] 按位与值 >= k 的最小值,如果找不到返回 -1
public int right(int l, int r, int k) {
int b = r, m, ans = -1;
while (l <= r) {
m = (l + r) >> 1;
int v = query(m, b);// 获取 nums[m..b] 的区间与运算值
if (v >= k) {
ans = v;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
// 获取 nums[l..r] 的区间与运算值
public int query(int l, int r) {
int lg = log[r - l + 1];
return st[l][lg] & st[r - (1 << lg) + 1][lg];
}
}
标签:return,nums,int,public,ans,3171,st,leetcode,解法
From: https://www.cnblogs.com/fishcanfly/p/18229802