3171. 找到按位与最接近 K 的子数组

2024-06-04 05:36
文章标签 数组 找到 接近 按位 3171

本文主要是介绍3171. 找到按位与最接近 K 的子数组,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

找子数组,尽量以 i 为右端点找性质 找子数组,尽量以i为右端点找性质 找子数组,尽量以i为右端点找性质
链接:Leetcode 400 周赛 D 题

位运算性质技巧

  • 子数组的与值 最多只有log(u)个,u=max_element(),
  • [l, r], [l+1,r], [l+2, r] …[r,r] 逻辑与值从右到左最多的变化就是
  • nums[r] -> nums[r]-一个二进制1, …所以最多log(nums[r])个不同的值
  • 上面是以r1为右端点 有log(nums[r1])个不同的值,那么复杂度就是nlog(max_element(nums))。
  • code

class Solution {
public:int minimumDifference(vector<int>& nums, int k) {set<int> a;int n = nums.size(); int res = 1e9;for(int i = 0; i < n; i ++){res = min(res, abs(nums[i]-k));set<int> b;for(auto it : a){b.insert(it&nums[i]);res = min(res, abs((it&nums[i])-k));}a = b;a.insert(nums[i]);}return res;}
};

滑动窗口

  • 因为要求子数组求与,所以数组长度越大值不增,长度越小值不减,所以具有单调性,我们可以用滑动窗口来写
  • code

const int N = 1e5 + 10;
int bits[N][32];
class Solution {
public:int minimumDifference(vector<int>& nums, int m) {int n = nums.size();memset(bits, 0, sizeof(bits));for(int i = 1; i <= n; i ++){for(int k = 0; k <= 30; k ++){if((nums[i-1] >> k) & 1){bits[i][k] ++;}}}// 滑动窗口,子数组单调性,越多越小 越小越大int l = 1, r = 1; int res = 1e9; int tem = 0;vector<int> a(33);for(; r <= n; r ++){int b = 0;for(int k = 0; k <= 30; k ++){if(bits[r][k]) a[k] ++;if(a[k] == (r-l+1)) b += (1 << k);}tem = b;res = min(res, abs(tem - m));while(l <= r && tem < m){for(int k = 0; k <= 30; k ++){if(bits[l][k]) a[k] --;}int b = 0;l ++;for(int k = 0; k <= 30; k ++){if(a[k] == r-l+1) b |= (1 << k);}tem = b;res = min(res, abs(tem - m));} }return res;}
};

  • 扩展一下,这里是统计每个数字每位上1的个数,也可以用前缀和和st表来实现该部分功能,但写起来会更麻烦。

前缀和+二分

  • 赛时想出来的思路,枚举子数组左端点两次二分找最接近k的值更新答案
  • code

const int N = 1e5 + 10;
int bits[N][32];
class Solution {
public:int minimumDifference(vector<int>& a, int k) {int n = a.size();for(int i = 1; i <= n; i ++){int j = i - 1;for(int k = 0; k <= 30; k ++){if((a[j] >> k) & 1) bits[i][k] = bits[i-1][k] + 1;else bits[i][k] = bits[i-1][k];   }}int res = 1e9;function<int(int, int)> fun = [&](int l, int r)->int{int y = 0;for(int i = 0; i <= 30; i ++){if((bits[r][i] - bits[l-1][i]) == (r-l+1)) y |= (1 << i);}return y;};function<bool(int, int, int)> check = [&](int l, int r, int fg) -> bool{if(fg == 1){if(fun(l, r) >= k) return true;//da yu x zui xiaoelse return false;}else{//xiao yu x zui daif(fun(l, r) <= k) return true;else return false;}};for(int i = 1; i <= n; i ++){int x = fun(i, i);if(x > k){int l = i, r = n;while(l < r){// 大于x的最小的int mid = (l+r+1) >> 1;if(check(i, mid, 1)){l = mid;}else{r = mid-1;}}res = min(res, abs(k - fun(i, l)));l = i, r = n;while(l < r)//小于x最大{int mid = (l + r) >> 1;if(check(i, mid, 2)){r = mid;}else{l = mid+1;}}res = min(res, abs(k - fun(i, l)));}else{res = min(res, k-x);}}return res;}
};
  • 扩展一下,除了两次二分找小于k的最大和大于k的最小还可以直接前缀和找abs(k-sum[l,r])的谷值,三分搜索即可

这篇关于3171. 找到按位与最接近 K 的子数组的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1029202

相关文章

vcpkg安装opencv中的特殊问题记录(无法找到opencv_corexd.dll)

我是按照网上的vcpkg安装opencv方法进行的(比如这篇:从0开始在visual studio上安装opencv(超详细,针对小白)),但是中间出现了一些别人没有遇到的问题,虽然原因没有找到,但是本人给出一些暂时的解决办法: 问题1: 我在安装库命令行使用的是 .\vcpkg.exe install opencv 我的电脑是x64,vcpkg在这条命令后默认下载的也是opencv2:x6

剑指offer(C++)--数组中只出现一次的数字

题目 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 class Solution {public:void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {int len = data.size();if(len<2)return;int one = 0;for(int i

IOS 数组去重的几种方式

本来只知道NSSet和KeyValues的。今天又新学了几种方式 还有就是和同事学的一种方式 外层循环从0开始遍历,内层从最后一个元素开始遍历 for(int i=0;i<index;i++){  for(int j=index-1;j>i;j-- ){ } }

Java基础(二)——数组,方法,方法重载

个人简介 👀个人主页: 前端杂货铺 ⚡开源项目: rich-vue3 (基于 Vue3 + TS + Pinia + Element Plus + Spring全家桶 + MySQL) 🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步至千里,积小流成江海 🥇推荐学习:🍖开源 rich-vue3 🍍前端面试

poj 3882(Stammering Aliens) 后缀数组 或者 hash

后缀数组:  构建后缀数组,注意要在字符串莫末尾加上一个没出现过的字符。然后可以2分或者直接扫描,直接扫描需要用单调队列来维护 VIEW CODE #include<cstdio>#include<algorithm>#include<iostream>#include<cmath>#include<queue>#include<stack>#include<string

poj 3294(Life Forms) 2分+ 后缀数组

我曾用字符串hash写,但是超时了。只能用后最数组了。大致思路:用不同的符号吧字符串连接起来,构建后缀数组,然后2分答案,依次扫描后缀数组,看是否瞒住条件。 VIEW CODE #include<cstdio>#include<vector>#include<cmath>#include<algorithm>#include<cstring>#include<cassert>#

C语言函数参数--数组长度

int read_column_numbers(int columns[], int max){} 在函数声明的数组参数中,并未指定数组的长度。这种格式是OK的,因为无论调用函数的程序传递给它的数组参数的长度是多少,这个函数都将照收不误。 这是一个伟大的特性,它允许单个函数操纵任意长度的一维数组。 这个特性不利的一面是函数没法知道该数组的长度。如果确实需要数组的长度,它的值必须作为一个单独的

从JavaScript 数组去重看兼容性问题,及性能优化(摘自玉伯博客)

缘由 JavaScript 数组去重经常出现在前端招聘的笔试题里,比如: 有数组 var arr = ['a', 'b', 'c', '1', 0, 'c', 1, '', 1, 0],请用 JavaScript 实现去重函数 unqiue,使得 unique(arr) 返回 ['a', 'b', 'c', '1', 0, 1, ''] 作为笔试题,考点有二: 正确。别小看这个考点

【Java】ArrayListString转化为String数组问题

Java的容器类Collections中toArray()方法,可以把诸如ArrayList<String>的动态数组、不定长转化静态数组、定长数组String[] 但是,如下的转化方式是错误的。 [java]  view plain copy String[] strArray = (String[]) arrayList.toArray();   如果这样执行会导致

数组 (java)

文章目录 一维数组静态初始化动态初始化 二维数组静态初始化动态初始化 数组参数传递可变参数关于 main 方法的形参 argsArray 工具类sort 中的 comparable 和 comparatorcomparator 比较器排序comparable 自然排序 一维数组 线性结构 静态初始化 第一种:int[] arr = {1,2,3,4},第二种:int[]