Always keep a beginner's mind, don't for|

fishcanfly

园龄:8年10个月粉丝:0关注:4

[leetcode 3171] 解法列表

  • 线段树解法 + 二分
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];
    }
       
}

本文作者:fishcanfly

本文链接:https://www.cnblogs.com/fishcanfly/p/18229802

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   fishcanfly  阅读(17)  评论(0编辑  收藏  举报
历史上的今天:
2022-06-03 【牛客小白月赛51 F平均题】数论,前缀和
2022-06-03 【剑指 Offer II 118. 多余的边】并查集求回路
2022-06-03 【879. 盈利计划】 动态规划
//雪花飘落效果
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起